mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-13 16:57:54 -05:00
Compare commits
17 Commits
2.0.0-beta
...
2.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
704924c3cd | ||
|
|
a7a6006533 | ||
|
|
58c608620e | ||
|
|
b1d2061cbc | ||
|
|
1f31073201 | ||
|
|
a3a1fb0dd7 | ||
|
|
26f6fa6570 | ||
|
|
0619a7a76c | ||
|
|
31cd782ba7 | ||
|
|
5e90b224c2 | ||
|
|
9a48566b24 | ||
|
|
76e70a6c81 | ||
|
|
5b7a7779fb | ||
|
|
48c7debe5a | ||
|
|
dc0fb85fd3 | ||
|
|
63b109a4f5 | ||
|
|
cd6daf0b79 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ test/*.js
|
||||
parser.output
|
||||
/node_modules
|
||||
npm-debug.log*
|
||||
yarn.lock
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
|
||||
* Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/coffeescript/tree/master/src). If you’re just getting started with CoffeeScript, there’s a nice [style guide](https://github.com/polarmobile/coffeescript-style-guide).
|
||||
|
||||
* In your pull request, do not add documentation to `index.html` or re-build the minified `coffeescript.js` file. We’ll do those things before cutting a new release.
|
||||
* In your pull request, do not add documentation to `index.html` or re-build the minified `coffeescript.js` file. We’ll do those things before cutting a new release. You _should,_ however, commit the updated compiled JavaScript files in `lib`.
|
||||
4
Cakefile
4
Cakefile
@@ -378,6 +378,10 @@ runTests = (CoffeeScript) ->
|
||||
# Convenience aliases.
|
||||
global.CoffeeScript = CoffeeScript
|
||||
global.Repl = require './lib/coffeescript/repl'
|
||||
global.bold = bold
|
||||
global.red = red
|
||||
global.green = green
|
||||
global.reset = reset
|
||||
|
||||
# Our test helper function for delimiting different test cases.
|
||||
global.test = (description, fn) ->
|
||||
|
||||
@@ -238,7 +238,10 @@ sources = []
|
||||
sourceCode = []
|
||||
notSources = {}
|
||||
watchedDirs = {}
|
||||
optionParser = <span class="hljs-literal">null</span></pre></div></div>
|
||||
optionParser = <span class="hljs-literal">null</span>
|
||||
|
||||
exports.buildCSOptionParser = buildCSOptionParser = <span class="hljs-function">-></span>
|
||||
<span class="hljs-keyword">new</span> optparse.OptionParser SWITCHES, BANNER</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
@@ -256,6 +259,7 @@ Many flags cause us to divert before compiling anything. Flags passed after
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre>exports.run = <span class="hljs-function">-></span>
|
||||
optionParser = buildCSOptionParser()
|
||||
parseOptions()</pre></div></div>
|
||||
|
||||
</li>
|
||||
@@ -771,7 +775,6 @@ same directory as the <code>.js</code> file.</p>
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">parseOptions</span> = -></span>
|
||||
optionParser = <span class="hljs-keyword">new</span> optparse.OptionParser SWITCHES, BANNER
|
||||
o = opts = optionParser.parse process.argv[<span class="hljs-number">2.</span>.]
|
||||
o.compile <span class="hljs-keyword">or</span>= !!o.output
|
||||
o.run = <span class="hljs-keyword">not</span> (o.compile <span class="hljs-keyword">or</span> o.<span class="hljs-built_in">print</span> <span class="hljs-keyword">or</span> o.map)
|
||||
@@ -856,7 +859,7 @@ shown.</p>
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">usage</span> = -></span>
|
||||
printLine (<span class="hljs-keyword">new</span> optparse.OptionParser SWITCHES, BANNER).help()</pre></div></div>
|
||||
printLine optionParser.help()</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
@@ -446,6 +446,7 @@ token stream.</p>
|
||||
|
||||
Identifier: [
|
||||
o <span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> IdentifierLiteral $<span class="hljs-number">1</span>
|
||||
o <span class="hljs-string">'CSX_TAG'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> CSXTag $<span class="hljs-number">1</span>
|
||||
]
|
||||
|
||||
Property: [
|
||||
@@ -541,6 +542,7 @@ the ordinary <strong>Assign</strong> is that these allow numbers and strings as
|
||||
|
||||
<div class="content"><div class='highlight'><pre> AssignObj: [
|
||||
o <span class="hljs-string">'ObjAssignable'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> Value $<span class="hljs-number">1</span>
|
||||
o <span class="hljs-string">'ObjRestValue'</span>
|
||||
o <span class="hljs-string">'ObjAssignable : Expression'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> Assign LOC(<span class="hljs-number">1</span>)(<span class="hljs-keyword">new</span> Value $<span class="hljs-number">1</span>), $<span class="hljs-number">3</span>, <span class="hljs-string">'object'</span>,
|
||||
operatorToken: LOC(<span class="hljs-number">2</span>)(<span class="hljs-keyword">new</span> Literal $<span class="hljs-number">2</span>)
|
||||
o <span class="hljs-string">'ObjAssignable :
|
||||
@@ -574,6 +576,40 @@ the ordinary <strong>Assign</strong> is that these allow numbers and strings as
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-22">¶</a>
|
||||
</div>
|
||||
<p>Object literal spread properties.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> ObjRestValue: [
|
||||
o <span class="hljs-string">'SimpleObjAssignable ...'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> Splat <span class="hljs-keyword">new</span> Value $<span class="hljs-number">1</span>
|
||||
o <span class="hljs-string">'ObjSpreadExpr ...'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> Splat $<span class="hljs-number">1</span>
|
||||
]
|
||||
|
||||
ObjSpreadExpr: [
|
||||
o <span class="hljs-string">'ObjSpreadIdentifier'</span>
|
||||
o <span class="hljs-string">'Object'</span>
|
||||
o <span class="hljs-string">'Parenthetical'</span>
|
||||
o <span class="hljs-string">'Super'</span>
|
||||
o <span class="hljs-string">'This'</span>
|
||||
o <span class="hljs-string">'SUPER Arguments'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> SuperCall LOC(<span class="hljs-number">1</span>)(<span class="hljs-keyword">new</span> Super), $<span class="hljs-number">2</span>
|
||||
o <span class="hljs-string">'SimpleObjAssignable Arguments'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> Call (<span class="hljs-keyword">new</span> Value $<span class="hljs-number">1</span>), $<span class="hljs-number">2</span>
|
||||
o <span class="hljs-string">'ObjSpreadExpr Arguments'</span>, <span class="hljs-function">-></span> <span class="hljs-keyword">new</span> Call $<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>
|
||||
]
|
||||
|
||||
ObjSpreadIdentifier: [
|
||||
o <span class="hljs-string">'SimpleObjAssignable . Property'</span>, <span class="hljs-function">-></span> (<span class="hljs-keyword">new</span> Value $<span class="hljs-number">1</span>).add(<span class="hljs-keyword">new</span> Access $<span class="hljs-number">3</span>)
|
||||
o <span class="hljs-string">'SimpleObjAssignable INDEX_START IndexValue INDEX_END'</span>, <span class="hljs-function">-></span> (<span class="hljs-keyword">new</span> Value $<span class="hljs-number">1</span>).add($<span class="hljs-number">3</span>)
|
||||
]</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-23">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-23">¶</a>
|
||||
</div>
|
||||
<p>A return statement from a function body.</p>
|
||||
|
||||
</div>
|
||||
@@ -596,11 +632,11 @@ the ordinary <strong>Assign</strong> is that these allow numbers and strings as
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-23">
|
||||
<li id="section-24">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-23">¶</a>
|
||||
<a class="pilcrow" href="#section-24">¶</a>
|
||||
</div>
|
||||
<p>A block comment.</p>
|
||||
|
||||
@@ -613,11 +649,11 @@ the ordinary <strong>Assign</strong> is that these allow numbers and strings as
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-24">
|
||||
<li id="section-25">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-24">¶</a>
|
||||
<a class="pilcrow" href="#section-25">¶</a>
|
||||
</div>
|
||||
<p>The <strong>Code</strong> node is the function literal. It’s defined by an indented block
|
||||
of <strong>Block</strong> preceded by a function arrow, with an optional parameter list.</p>
|
||||
@@ -632,11 +668,11 @@ of <strong>Block</strong> preceded by a function arrow, with an optional paramet
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-25">
|
||||
<li id="section-26">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-25">¶</a>
|
||||
<a class="pilcrow" href="#section-26">¶</a>
|
||||
</div>
|
||||
<p>CoffeeScript has two different symbols for functions. <code>-></code> is for ordinary
|
||||
functions, and <code>=></code> is for functions bound to the current value of <em>this</em>.</p>
|
||||
@@ -651,11 +687,11 @@ functions, and <code>=></code> is for functions bound to the current value of
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-26">
|
||||
<li id="section-27">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-26">¶</a>
|
||||
<a class="pilcrow" href="#section-27">¶</a>
|
||||
</div>
|
||||
<p>An optional, trailing comma.</p>
|
||||
|
||||
@@ -669,11 +705,11 @@ functions, and <code>=></code> is for functions bound to the current value of
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-27">
|
||||
<li id="section-28">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-27">¶</a>
|
||||
<a class="pilcrow" href="#section-28">¶</a>
|
||||
</div>
|
||||
<p>The list of parameters that a function accepts can be of any length.</p>
|
||||
|
||||
@@ -690,11 +726,11 @@ functions, and <code>=></code> is for functions bound to the current value of
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-28">
|
||||
<li id="section-29">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-28">¶</a>
|
||||
<a class="pilcrow" href="#section-29">¶</a>
|
||||
</div>
|
||||
<p>A single parameter in a function definition can be ordinary, or a splat
|
||||
that hoovers up the remaining arguments.</p>
|
||||
@@ -711,11 +747,11 @@ that hoovers up the remaining arguments.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-29">
|
||||
<li id="section-30">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-29">¶</a>
|
||||
<a class="pilcrow" href="#section-30">¶</a>
|
||||
</div>
|
||||
<p>Function Parameters</p>
|
||||
|
||||
@@ -731,11 +767,11 @@ that hoovers up the remaining arguments.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-30">
|
||||
<li id="section-31">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-30">¶</a>
|
||||
<a class="pilcrow" href="#section-31">¶</a>
|
||||
</div>
|
||||
<p>A splat that occurs outside of a parameter list.</p>
|
||||
|
||||
@@ -748,11 +784,11 @@ that hoovers up the remaining arguments.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-31">
|
||||
<li id="section-32">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-31">¶</a>
|
||||
<a class="pilcrow" href="#section-32">¶</a>
|
||||
</div>
|
||||
<p>Variables and properties that can be assigned to.</p>
|
||||
|
||||
@@ -768,11 +804,11 @@ that hoovers up the remaining arguments.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-32">
|
||||
<li id="section-33">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-32">¶</a>
|
||||
<a class="pilcrow" href="#section-33">¶</a>
|
||||
</div>
|
||||
<p>Everything that can be assigned to.</p>
|
||||
|
||||
@@ -787,11 +823,11 @@ that hoovers up the remaining arguments.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-33">
|
||||
<li id="section-34">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-33">¶</a>
|
||||
<a class="pilcrow" href="#section-34">¶</a>
|
||||
</div>
|
||||
<p>The types of things that can be treated as values – assigned to, invoked
|
||||
as functions, indexed into, named as a class, etc.</p>
|
||||
@@ -810,11 +846,11 @@ as functions, indexed into, named as a class, etc.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-34">
|
||||
<li id="section-35">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-34">¶</a>
|
||||
<a class="pilcrow" href="#section-35">¶</a>
|
||||
</div>
|
||||
<p>A <code>super</code>-based expression that can be used as a value.</p>
|
||||
|
||||
@@ -828,11 +864,11 @@ as functions, indexed into, named as a class, etc.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-35">
|
||||
<li id="section-36">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-35">¶</a>
|
||||
<a class="pilcrow" href="#section-36">¶</a>
|
||||
</div>
|
||||
<p>The general group of accessors into an object, by property, by prototype
|
||||
or by array index or slice.</p>
|
||||
@@ -851,11 +887,11 @@ or by array index or slice.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-36">
|
||||
<li id="section-37">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-36">¶</a>
|
||||
<a class="pilcrow" href="#section-37">¶</a>
|
||||
</div>
|
||||
<p>Indexing into an object or array using bracket notation.</p>
|
||||
|
||||
@@ -874,11 +910,11 @@ or by array index or slice.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-37">
|
||||
<li id="section-38">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-37">¶</a>
|
||||
<a class="pilcrow" href="#section-38">¶</a>
|
||||
</div>
|
||||
<p>In CoffeeScript, an object literal is simply a list of assignments.</p>
|
||||
|
||||
@@ -891,11 +927,11 @@ or by array index or slice.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-38">
|
||||
<li id="section-39">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-38">¶</a>
|
||||
<a class="pilcrow" href="#section-39">¶</a>
|
||||
</div>
|
||||
<p>Assignment of properties within an object literal can be separated by
|
||||
comma, as in JavaScript, or simply by newline.</p>
|
||||
@@ -913,11 +949,11 @@ comma, as in JavaScript, or simply by newline.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-39">
|
||||
<li id="section-40">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-39">¶</a>
|
||||
<a class="pilcrow" href="#section-40">¶</a>
|
||||
</div>
|
||||
<p>Class definitions have optional bodies of prototype property assignments,
|
||||
and optional references to the superclass.</p>
|
||||
@@ -1002,11 +1038,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-40">
|
||||
<li id="section-41">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-40">¶</a>
|
||||
<a class="pilcrow" href="#section-41">¶</a>
|
||||
</div>
|
||||
<p>Ordinary function invocation, or a chained series of calls.</p>
|
||||
|
||||
@@ -1022,11 +1058,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-41">
|
||||
<li id="section-42">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-41">¶</a>
|
||||
<a class="pilcrow" href="#section-42">¶</a>
|
||||
</div>
|
||||
<p>An optional existence check on a function.</p>
|
||||
|
||||
@@ -1040,11 +1076,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-42">
|
||||
<li id="section-43">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-42">¶</a>
|
||||
<a class="pilcrow" href="#section-43">¶</a>
|
||||
</div>
|
||||
<p>The list of arguments to a function call.</p>
|
||||
|
||||
@@ -1058,11 +1094,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-43">
|
||||
<li id="section-44">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-43">¶</a>
|
||||
<a class="pilcrow" href="#section-44">¶</a>
|
||||
</div>
|
||||
<p>A reference to the <em>this</em> current object.</p>
|
||||
|
||||
@@ -1076,11 +1112,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-44">
|
||||
<li id="section-45">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-44">¶</a>
|
||||
<a class="pilcrow" href="#section-45">¶</a>
|
||||
</div>
|
||||
<p>A reference to a property on <em>this</em>.</p>
|
||||
|
||||
@@ -1093,11 +1129,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-45">
|
||||
<li id="section-46">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-45">¶</a>
|
||||
<a class="pilcrow" href="#section-46">¶</a>
|
||||
</div>
|
||||
<p>The array literal.</p>
|
||||
|
||||
@@ -1111,11 +1147,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-46">
|
||||
<li id="section-47">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-46">¶</a>
|
||||
<a class="pilcrow" href="#section-47">¶</a>
|
||||
</div>
|
||||
<p>Inclusive and exclusive range dots.</p>
|
||||
|
||||
@@ -1129,11 +1165,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-47">
|
||||
<li id="section-48">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-47">¶</a>
|
||||
<a class="pilcrow" href="#section-48">¶</a>
|
||||
</div>
|
||||
<p>The CoffeeScript range literal.</p>
|
||||
|
||||
@@ -1146,11 +1182,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-48">
|
||||
<li id="section-49">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-48">¶</a>
|
||||
<a class="pilcrow" href="#section-49">¶</a>
|
||||
</div>
|
||||
<p>Array slice literals.</p>
|
||||
|
||||
@@ -1166,11 +1202,11 @@ and optional references to the superclass.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-49">
|
||||
<li id="section-50">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-49">¶</a>
|
||||
<a class="pilcrow" href="#section-50">¶</a>
|
||||
</div>
|
||||
<p>The <strong>ArgList</strong> is both the list of objects passed into a function call,
|
||||
as well as the contents of an array literal
|
||||
@@ -1189,11 +1225,11 @@ as well as the contents of an array literal
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-50">
|
||||
<li id="section-51">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-50">¶</a>
|
||||
<a class="pilcrow" href="#section-51">¶</a>
|
||||
</div>
|
||||
<p>Valid arguments are Blocks or Splats.</p>
|
||||
|
||||
@@ -1208,11 +1244,11 @@ as well as the contents of an array literal
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-51">
|
||||
<li id="section-52">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-51">¶</a>
|
||||
<a class="pilcrow" href="#section-52">¶</a>
|
||||
</div>
|
||||
<p>Just simple, comma-separated, required arguments (no fancy syntax). We need
|
||||
this to be separate from the <strong>ArgList</strong> for use in <strong>Switch</strong> blocks, where
|
||||
@@ -1228,11 +1264,11 @@ having the newlines wouldn’t make sense.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-52">
|
||||
<li id="section-53">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-52">¶</a>
|
||||
<a class="pilcrow" href="#section-53">¶</a>
|
||||
</div>
|
||||
<p>The variants of <em>try/catch/finally</em> exception handling blocks.</p>
|
||||
|
||||
@@ -1248,11 +1284,11 @@ having the newlines wouldn’t make sense.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-53">
|
||||
<li id="section-54">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-53">¶</a>
|
||||
<a class="pilcrow" href="#section-54">¶</a>
|
||||
</div>
|
||||
<p>A catch clause names its error and runs a block of code.</p>
|
||||
|
||||
@@ -1267,11 +1303,11 @@ having the newlines wouldn’t make sense.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-54">
|
||||
<li id="section-55">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-54">¶</a>
|
||||
<a class="pilcrow" href="#section-55">¶</a>
|
||||
</div>
|
||||
<p>Throw an exception object.</p>
|
||||
|
||||
@@ -1284,11 +1320,11 @@ having the newlines wouldn’t make sense.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-55">
|
||||
<li id="section-56">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-55">¶</a>
|
||||
<a class="pilcrow" href="#section-56">¶</a>
|
||||
</div>
|
||||
<p>Parenthetical expressions. Note that the <strong>Parenthetical</strong> is a <strong>Value</strong>,
|
||||
not an <strong>Expression</strong>, so if you need to use an expression in a place
|
||||
@@ -1305,11 +1341,11 @@ the trick.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-56">
|
||||
<li id="section-57">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-56">¶</a>
|
||||
<a class="pilcrow" href="#section-57">¶</a>
|
||||
</div>
|
||||
<p>The condition portion of a while loop.</p>
|
||||
|
||||
@@ -1325,11 +1361,11 @@ the trick.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-57">
|
||||
<li id="section-58">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-57">¶</a>
|
||||
<a class="pilcrow" href="#section-58">¶</a>
|
||||
</div>
|
||||
<p>The while loop can either be normal, with a block of expressions to execute,
|
||||
or postfix, with a single expression. There is no do..while.</p>
|
||||
@@ -1351,11 +1387,11 @@ or postfix, with a single expression. There is no do..while.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-58">
|
||||
<li id="section-59">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-58">¶</a>
|
||||
<a class="pilcrow" href="#section-59">¶</a>
|
||||
</div>
|
||||
<p>Array, object, and range comprehensions, at the most generic level.
|
||||
Comprehensions can either be normal, with a block of expressions to execute,
|
||||
@@ -1383,11 +1419,11 @@ or postfix, with a single expression.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-59">
|
||||
<li id="section-60">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-59">¶</a>
|
||||
<a class="pilcrow" href="#section-60">¶</a>
|
||||
</div>
|
||||
<p>An array of all accepted values for a variable inside the loop.
|
||||
This enables support for pattern matching.</p>
|
||||
@@ -1404,11 +1440,11 @@ This enables support for pattern matching.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-60">
|
||||
<li id="section-61">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-60">¶</a>
|
||||
<a class="pilcrow" href="#section-61">¶</a>
|
||||
</div>
|
||||
<p>An array or range comprehension has variables for the current element
|
||||
and (optional) reference to the current index. Or, <em>key, value</em>, in the case
|
||||
@@ -1424,11 +1460,11 @@ of object comprehensions.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-61">
|
||||
<li id="section-62">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-61">¶</a>
|
||||
<a class="pilcrow" href="#section-62">¶</a>
|
||||
</div>
|
||||
<p>The source of a comprehension is an array or object with an optional guard
|
||||
clause. If it’s an array comprehension, you can also choose to step through
|
||||
@@ -1463,11 +1499,11 @@ in fixed-size increments.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-62">
|
||||
<li id="section-63">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-62">¶</a>
|
||||
<a class="pilcrow" href="#section-63">¶</a>
|
||||
</div>
|
||||
<p>An individual <strong>When</strong> clause, with action.</p>
|
||||
|
||||
@@ -1481,11 +1517,11 @@ in fixed-size increments.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-63">
|
||||
<li id="section-64">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-63">¶</a>
|
||||
<a class="pilcrow" href="#section-64">¶</a>
|
||||
</div>
|
||||
<p>The most basic form of <em>if</em> is a condition and an action. The following
|
||||
if-related rules are broken up along these lines in order to avoid
|
||||
@@ -1501,11 +1537,11 @@ ambiguity.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-64">
|
||||
<li id="section-65">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-64">¶</a>
|
||||
<a class="pilcrow" href="#section-65">¶</a>
|
||||
</div>
|
||||
<p>The full complement of <em>if</em> expressions, including postfix one-liner
|
||||
<em>if</em> and <em>unless</em>.</p>
|
||||
@@ -1522,11 +1558,11 @@ ambiguity.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-65">
|
||||
<li id="section-66">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-65">¶</a>
|
||||
<a class="pilcrow" href="#section-66">¶</a>
|
||||
</div>
|
||||
<p>Arithmetic and logical operators, working on one or more operands.
|
||||
Here they are grouped by order of precedence. The actual precedence rules
|
||||
@@ -1553,11 +1589,11 @@ rules are necessary.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-66">
|
||||
<li id="section-67">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-66">¶</a>
|
||||
<a class="pilcrow" href="#section-67">¶</a>
|
||||
</div>
|
||||
<p><a href="http://coffeescript.org/#existential-operator">The existential operator</a>.</p>
|
||||
|
||||
@@ -1595,26 +1631,14 @@ rules are necessary.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-67">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-67">¶</a>
|
||||
</div>
|
||||
<h2 id="precedence">Precedence</h2>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-68">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-68">¶</a>
|
||||
</div>
|
||||
|
||||
<h2 id="precedence">Precedence</h2>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
@@ -1626,6 +1650,18 @@ rules are necessary.</p>
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-69">¶</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-70">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-70">¶</a>
|
||||
</div>
|
||||
<p>Operators at the top of this list have higher precedence than the ones lower
|
||||
down. Following these rules is what makes <code>2 + 3 * 4</code> parse as:</p>
|
||||
<pre><code><span class="hljs-number">2</span> + (<span class="hljs-number">3</span> * <span class="hljs-number">4</span>)
|
||||
@@ -1665,26 +1701,14 @@ down. Following these rules is what makes <code>2 + 3 * 4</code> parse as:</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-70">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-70">¶</a>
|
||||
</div>
|
||||
<h2 id="wrapping-up">Wrapping Up</h2>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-71">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-71">¶</a>
|
||||
</div>
|
||||
|
||||
<h2 id="wrapping-up">Wrapping Up</h2>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
@@ -1696,6 +1720,18 @@ down. Following these rules is what makes <code>2 + 3 * 4</code> parse as:</p>
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-72">¶</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-73">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-73">¶</a>
|
||||
</div>
|
||||
<p>Finally, now that we have our <strong>grammar</strong> and our <strong>operators</strong>, we can create
|
||||
our <strong>Jison.Parser</strong>. We do this by processing all of our rules, recording all
|
||||
terminals (every symbol which does not appear as the name of a rule above)
|
||||
@@ -1714,11 +1750,11 @@ as “tokens”.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-73">
|
||||
<li id="section-74">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-73">¶</a>
|
||||
<a class="pilcrow" href="#section-74">¶</a>
|
||||
</div>
|
||||
<p>Initialize the <strong>Parser</strong> with our list of terminal <strong>tokens</strong>, our <strong>grammar</strong>
|
||||
rules, and the name of the root. Reverse the operators because Jison orders
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -135,6 +135,8 @@ Use it like so:</p>
|
||||
options = parser.parse process.argv
|
||||
</code></pre><p>The first non-option is considered to be the start of the file (and file
|
||||
option) list, and all subsequent arguments are left unparsed.</p>
|
||||
<p>The <code>coffee</code> command uses an instance of <strong>OptionParser</strong> to parse its
|
||||
command-line arguments in <code>src/command.coffee</code>.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ Unwrap that too.</p>
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> ast = <span class="hljs-keyword">new</span> Block [
|
||||
<span class="hljs-keyword">new</span> Assign (<span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Literal <span class="hljs-string">'_'</span>), ast, <span class="hljs-string">'='</span>
|
||||
<span class="hljs-keyword">new</span> Assign (<span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Literal <span class="hljs-string">'__'</span>), ast, <span class="hljs-string">'='</span>
|
||||
]
|
||||
js = ast.compile {bare: <span class="hljs-literal">yes</span>, locals: Object.keys(context), referencedVars}
|
||||
cb <span class="hljs-literal">null</span>, runInContext js, context, filename
|
||||
|
||||
@@ -124,6 +124,9 @@ parentheses, and generally clean things up.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre>
|
||||
{throwSyntaxError} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./helpers'</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
@@ -199,6 +202,7 @@ corrected before implicit parentheses can be wrapped around blocks of code.</p>
|
||||
@tagPostfixConditionals()
|
||||
@addImplicitBracesAndParens()
|
||||
@addLocationDataToGeneratedTokens()
|
||||
@enforceValidCSXAttributes()
|
||||
@fixOutdentLocationData()
|
||||
@tokens</pre></div></div>
|
||||
|
||||
@@ -225,16 +229,18 @@ our feet.</p>
|
||||
i += block.call <span class="hljs-keyword">this</span>, token, i, tokens <span class="hljs-keyword">while</span> token = tokens[i]
|
||||
<span class="hljs-literal">true</span>
|
||||
|
||||
detectEnd: <span class="hljs-function"><span class="hljs-params">(i, condition, action)</span> -></span>
|
||||
detectEnd: <span class="hljs-function"><span class="hljs-params">(i, condition, action, opts = {})</span> -></span>
|
||||
{tokens} = <span class="hljs-keyword">this</span>
|
||||
levels = <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">while</span> token = tokens[i]
|
||||
<span class="hljs-keyword">return</span> action.call <span class="hljs-keyword">this</span>, token, i <span class="hljs-keyword">if</span> levels <span class="hljs-keyword">is</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> condition.call <span class="hljs-keyword">this</span>, token, i
|
||||
<span class="hljs-keyword">return</span> action.call <span class="hljs-keyword">this</span>, token, i - <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> token <span class="hljs-keyword">or</span> levels < <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">return</span> action.call <span class="hljs-keyword">this</span>, token, i <span class="hljs-keyword">if</span> levels <span class="hljs-keyword">is</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> condition.call <span class="hljs-keyword">this</span>, token, i
|
||||
<span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> EXPRESSION_START
|
||||
levels += <span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> EXPRESSION_END
|
||||
levels -= <span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">if</span> levels < <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> opts.returnOnNegativeLevel
|
||||
<span class="hljs-keyword">return</span> action.call <span class="hljs-keyword">this</span>, token, i
|
||||
i += <span class="hljs-number">1</span>
|
||||
i - <span class="hljs-number">1</span></pre></div></div>
|
||||
|
||||
@@ -266,18 +272,16 @@ dispatch them here.</p>
|
||||
<a class="pilcrow" href="#section-8">¶</a>
|
||||
</div>
|
||||
<p>The lexer has tagged the opening parenthesis of a method call. Match it with
|
||||
its paired close. We have the mis-nested outdent case included here for
|
||||
calls that close on the same line, just before their outdent.</p>
|
||||
its paired close.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> closeOpenCalls: <span class="hljs-function">-></span>
|
||||
<span class="hljs-function"> <span class="hljs-title">condition</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||||
token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">')'</span>, <span class="hljs-string">'CALL_END'</span>] <span class="hljs-keyword">or</span>
|
||||
token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">and</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">')'</span>
|
||||
token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">')'</span>, <span class="hljs-string">'CALL_END'</span>]
|
||||
<span class="hljs-function">
|
||||
<span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||||
@tokens[<span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">then</span> i - <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> i][<span class="hljs-number">0</span>] = <span class="hljs-string">'CALL_END'</span>
|
||||
token[<span class="hljs-number">0</span>] = <span class="hljs-string">'CALL_END'</span>
|
||||
|
||||
@scanTokens (token, i) ->
|
||||
@detectEnd i + <span class="hljs-number">1</span>, condition, action <span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'CALL_START'</span>
|
||||
@@ -292,7 +296,7 @@ calls that close on the same line, just before their outdent.</p>
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-9">¶</a>
|
||||
</div>
|
||||
<p>The lexer has tagged the opening parenthesis of an indexing operation call.
|
||||
<p>The lexer has tagged the opening bracket of an indexing operation call.
|
||||
Match it with its paired close.</p>
|
||||
|
||||
</div>
|
||||
@@ -417,7 +421,7 @@ add them.</p>
|
||||
@scanTokens (token, i, tokens) ->
|
||||
[tag] = token
|
||||
[prevTag] = prevToken = <span class="hljs-keyword">if</span> i > <span class="hljs-number">0</span> <span class="hljs-keyword">then</span> tokens[i - <span class="hljs-number">1</span>] <span class="hljs-keyword">else</span> []
|
||||
[nextTag] = <span class="hljs-keyword">if</span> i < tokens.length - <span class="hljs-number">1</span> <span class="hljs-keyword">then</span> tokens[i + <span class="hljs-number">1</span>] <span class="hljs-keyword">else</span> []
|
||||
[nextTag] = nextToken = <span class="hljs-keyword">if</span> i < tokens.length - <span class="hljs-number">1</span> <span class="hljs-keyword">then</span> tokens[i + <span class="hljs-number">1</span>] <span class="hljs-keyword">else</span> []
|
||||
<span class="hljs-function"> <span class="hljs-title">stackTop</span> = -></span> stack[stack.length - <span class="hljs-number">1</span>]
|
||||
startIdx = i</pre></div></div>
|
||||
|
||||
@@ -473,30 +477,35 @@ class declaration or if-conditionals)</p>
|
||||
|
||||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">inImplicitControl</span> = -></span> inImplicit() <span class="hljs-keyword">and</span> stackTop()?[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'CONTROL'</span>
|
||||
<span class="hljs-function">
|
||||
<span class="hljs-title">startImplicitCall</span> = <span class="hljs-params">(j)</span> -></span>
|
||||
idx = j ? i
|
||||
<span class="hljs-title">startImplicitCall</span> = <span class="hljs-params">(idx)</span> -></span>
|
||||
stack.push [<span class="hljs-string">'('</span>, idx, ours: <span class="hljs-literal">yes</span>]
|
||||
tokens.splice idx, <span class="hljs-number">0</span>, generate <span class="hljs-string">'CALL_START'</span>, <span class="hljs-string">'('</span>
|
||||
i += <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> j?
|
||||
<span class="hljs-function">
|
||||
<span class="hljs-title">endImplicitCall</span> = -></span>
|
||||
stack.pop()
|
||||
tokens.splice i, <span class="hljs-number">0</span>, generate <span class="hljs-string">'CALL_END'</span>, <span class="hljs-string">')'</span>, [<span class="hljs-string">''</span>, <span class="hljs-string">'end of input'</span>, token[<span class="hljs-number">2</span>]]
|
||||
i += <span class="hljs-number">1</span>
|
||||
<span class="hljs-function">
|
||||
<span class="hljs-title">startImplicitObject</span> = <span class="hljs-params">(j, startsLine = <span class="hljs-literal">yes</span>)</span> -></span>
|
||||
idx = j ? i
|
||||
<span class="hljs-title">startImplicitObject</span> = <span class="hljs-params">(idx, startsLine = <span class="hljs-literal">yes</span>)</span> -></span>
|
||||
stack.push [<span class="hljs-string">'{'</span>, idx, sameLine: <span class="hljs-literal">yes</span>, startsLine: startsLine, ours: <span class="hljs-literal">yes</span>]
|
||||
val = <span class="hljs-keyword">new</span> String <span class="hljs-string">'{'</span>
|
||||
val.generated = <span class="hljs-literal">yes</span>
|
||||
tokens.splice idx, <span class="hljs-number">0</span>, generate <span class="hljs-string">'{'</span>, val, token
|
||||
i += <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> j?
|
||||
<span class="hljs-function">
|
||||
<span class="hljs-title">endImplicitObject</span> = <span class="hljs-params">(j)</span> -></span>
|
||||
j = j ? i
|
||||
stack.pop()
|
||||
tokens.splice j, <span class="hljs-number">0</span>, generate <span class="hljs-string">'}'</span>, <span class="hljs-string">'}'</span>, token
|
||||
i += <span class="hljs-number">1</span></pre></div></div>
|
||||
i += <span class="hljs-number">1</span>
|
||||
<span class="hljs-function">
|
||||
<span class="hljs-title">implicitObjectContinues</span> = <span class="hljs-params">(j)</span> =></span>
|
||||
nextTerminatorIdx = <span class="hljs-literal">null</span>
|
||||
@detectEnd j,
|
||||
<span class="hljs-function"><span class="hljs-params">(token)</span> -></span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span>
|
||||
(token, i) -> nextTerminatorIdx = i
|
||||
returnOnNegativeLevel: <span class="hljs-literal">yes</span>
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> nextTerminatorIdx?
|
||||
@looksObjectish nextTerminatorIdx + <span class="hljs-number">1</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
@@ -507,12 +516,14 @@ class declaration or if-conditionals)</p>
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-18">¶</a>
|
||||
</div>
|
||||
<p>Don’t end an implicit call on next indent if any of these are in an argument</p>
|
||||
<p>Don’t end an implicit call/object on next indent if any of these are in an argument/value</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> inImplicitCall() <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">in</span> [<span class="hljs-string">'IF'</span>, <span class="hljs-string">'TRY'</span>, <span class="hljs-string">'FINALLY'</span>, <span class="hljs-string">'CATCH'</span>,
|
||||
<span class="hljs-string">'CLASS'</span>, <span class="hljs-string">'SWITCH'</span>]
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (
|
||||
(inImplicitCall() <span class="hljs-keyword">or</span> inImplicitObject()) <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">in</span> CONTROL_IN_IMPLICIT <span class="hljs-keyword">or</span>
|
||||
inImplicitObject() <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">is</span> <span class="hljs-string">':'</span> <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'FOR'</span>
|
||||
)
|
||||
stack.push [<span class="hljs-string">'CONTROL'</span>, i, ours: <span class="hljs-literal">yes</span>]
|
||||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">1</span>)
|
||||
|
||||
@@ -535,8 +546,12 @@ class declaration or if-conditionals)</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> prevTag <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">'=>'</span>, <span class="hljs-string">'->'</span>, <span class="hljs-string">'['</span>, <span class="hljs-string">'('</span>, <span class="hljs-string">','</span>, <span class="hljs-string">'{'</span>, <span class="hljs-string">'TRY'</span>, <span class="hljs-string">'ELSE'</span>, <span class="hljs-string">'='</span>]
|
||||
endImplicitCall() <span class="hljs-keyword">while</span> inImplicitCall()
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> prevTag <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">'=>'</span>, <span class="hljs-string">'->'</span>, <span class="hljs-string">'['</span>, <span class="hljs-string">'('</span>, <span class="hljs-string">','</span>, <span class="hljs-string">'{'</span>, <span class="hljs-string">'ELSE'</span>, <span class="hljs-string">'='</span>]
|
||||
<span class="hljs-keyword">while</span> inImplicitCall() <span class="hljs-keyword">or</span> inImplicitObject() <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">':'</span>
|
||||
<span class="hljs-keyword">if</span> inImplicitCall()
|
||||
endImplicitCall()
|
||||
<span class="hljs-keyword">else</span>
|
||||
endImplicitObject()
|
||||
stack.pop() <span class="hljs-keyword">if</span> inImplicitControl()
|
||||
stack.push [tag, i]
|
||||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">1</span>)</pre></div></div>
|
||||
@@ -599,7 +614,7 @@ f a, f() b, f? c, h[0] d etc.</p>
|
||||
tag <span class="hljs-keyword">is</span> <span class="hljs-string">'?'</span> <span class="hljs-keyword">and</span> i > <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> tokens[i - <span class="hljs-number">1</span>].spaced) <span class="hljs-keyword">and</span>
|
||||
(nextTag <span class="hljs-keyword">in</span> IMPLICIT_CALL <span class="hljs-keyword">or</span>
|
||||
nextTag <span class="hljs-keyword">in</span> IMPLICIT_UNSPACED_CALL <span class="hljs-keyword">and</span>
|
||||
<span class="hljs-keyword">not</span> tokens[i + <span class="hljs-number">1</span>]?.spaced <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> tokens[i + <span class="hljs-number">1</span>]?.newLine)
|
||||
<span class="hljs-keyword">not</span> nextToken.spaced <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> nextToken.newLine)
|
||||
tag = token[<span class="hljs-number">0</span>] = <span class="hljs-string">'FUNC_EXIST'</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'?'</span>
|
||||
startImplicitCall i + <span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">2</span>)</pre></div></div>
|
||||
@@ -617,11 +632,6 @@ f a, f() b, f? c, h[0] d etc.</p>
|
||||
<pre><code>f
|
||||
a: b
|
||||
c: d
|
||||
</code></pre><p>and</p>
|
||||
<pre><code>f
|
||||
<span class="hljs-number">1</span>
|
||||
a: b
|
||||
b: c
|
||||
</code></pre><p>Don’t accept implicit calls of this type, when on the same line
|
||||
as the control structures below as that may misinterpret constructs like:</p>
|
||||
<pre><code><span class="hljs-keyword">if</span> f
|
||||
@@ -674,7 +684,9 @@ that creates grammatical ambiguities.</p>
|
||||
<span class="hljs-keyword">when</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> EXPRESSION_END <span class="hljs-keyword">then</span> start[<span class="hljs-number">1</span>]
|
||||
<span class="hljs-keyword">when</span> @tag(i - <span class="hljs-number">2</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'@'</span> <span class="hljs-keyword">then</span> i - <span class="hljs-number">2</span>
|
||||
<span class="hljs-keyword">else</span> i - <span class="hljs-number">1</span>
|
||||
s -= <span class="hljs-number">2</span> <span class="hljs-keyword">while</span> @tag(s - <span class="hljs-number">2</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'HERECOMMENT'</span></pre></div></div>
|
||||
s -= <span class="hljs-number">2</span> <span class="hljs-keyword">while</span> @tag(s - <span class="hljs-number">2</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'HERECOMMENT'</span>
|
||||
|
||||
startsLine = s <span class="hljs-keyword">is</span> <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> @tag(s - <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> LINEBREAKS <span class="hljs-keyword">or</span> tokens[s - <span class="hljs-number">1</span>].newLine</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
@@ -685,23 +697,6 @@ that creates grammatical ambiguities.</p>
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-26">¶</a>
|
||||
</div>
|
||||
<p>Mark if the value is a for loop</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> @insideForDeclaration = nextTag <span class="hljs-keyword">is</span> <span class="hljs-string">'FOR'</span>
|
||||
|
||||
startsLine = s <span class="hljs-keyword">is</span> <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> @tag(s - <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> LINEBREAKS <span class="hljs-keyword">or</span> tokens[s - <span class="hljs-number">1</span>].newLine</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-27">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-27">¶</a>
|
||||
</div>
|
||||
<p>Are we just continuing an already declared object?</p>
|
||||
|
||||
</div>
|
||||
@@ -718,11 +713,11 @@ that creates grammatical ambiguities.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-28">
|
||||
<li id="section-27">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-28">¶</a>
|
||||
<a class="pilcrow" href="#section-27">¶</a>
|
||||
</div>
|
||||
<p>End implicit calls when chaining method calls
|
||||
like e.g.:</p>
|
||||
@@ -741,11 +736,11 @@ like e.g.:</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-29">
|
||||
<li id="section-28">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-29">¶</a>
|
||||
<a class="pilcrow" href="#section-28">¶</a>
|
||||
</div>
|
||||
<p>Mark all enclosing objects as not sameLine</p>
|
||||
|
||||
@@ -763,11 +758,11 @@ like e.g.:</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-30">
|
||||
<li id="section-29">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-30">¶</a>
|
||||
<a class="pilcrow" href="#section-29">¶</a>
|
||||
</div>
|
||||
<p>Close implicit calls when reached end of argument list</p>
|
||||
|
||||
@@ -779,29 +774,30 @@ like e.g.:</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-31">
|
||||
<li id="section-30">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-31">¶</a>
|
||||
<a class="pilcrow" href="#section-30">¶</a>
|
||||
</div>
|
||||
<p>Close implicit objects such as:
|
||||
return a: 1, b: 2 unless true</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> inImplicitObject() <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @insideForDeclaration <span class="hljs-keyword">and</span> sameLine <span class="hljs-keyword">and</span>
|
||||
tag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">':'</span>
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> inImplicitObject() <span class="hljs-keyword">and</span> sameLine <span class="hljs-keyword">and</span>
|
||||
tag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">':'</span> <span class="hljs-keyword">and</span>
|
||||
<span class="hljs-keyword">not</span> (tag <span class="hljs-keyword">in</span> [<span class="hljs-string">'POST_IF'</span>, <span class="hljs-string">'FOR'</span>, <span class="hljs-string">'WHILE'</span>, <span class="hljs-string">'UNTIL'</span>] <span class="hljs-keyword">and</span> startsLine <span class="hljs-keyword">and</span> implicitObjectContinues(i + <span class="hljs-number">1</span>))
|
||||
endImplicitObject()</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-32">
|
||||
<li id="section-31">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-32">¶</a>
|
||||
<a class="pilcrow" href="#section-31">¶</a>
|
||||
</div>
|
||||
<p>Close implicit objects when at end of line, line didn’t end with a comma
|
||||
and the implicit object didn’t start the line or the next line doesn’t look like
|
||||
@@ -819,11 +815,11 @@ the continuation of an object.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-33">
|
||||
<li id="section-32">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-33">¶</a>
|
||||
<a class="pilcrow" href="#section-32">¶</a>
|
||||
</div>
|
||||
<p>Close implicit object if comma is the last character
|
||||
and what comes after doesn’t look like it belongs.
|
||||
@@ -838,17 +834,16 @@ e = <span class="hljs-number">2</span>
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @looksObjectish(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">and</span> inImplicitObject() <span class="hljs-keyword">and</span>
|
||||
<span class="hljs-keyword">not</span> @insideForDeclaration <span class="hljs-keyword">and</span>
|
||||
(nextTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> @looksObjectish(i + <span class="hljs-number">2</span>))</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-34">
|
||||
<li id="section-33">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-34">¶</a>
|
||||
<a class="pilcrow" href="#section-33">¶</a>
|
||||
</div>
|
||||
<p>When nextTag is OUTDENT the comma is insignificant and
|
||||
should just be ignored so embed it in the implicit object.</p>
|
||||
@@ -866,6 +861,27 @@ array further up the stack, so give it a chance.</p>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-34">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-34">¶</a>
|
||||
</div>
|
||||
<p>Make sure only strings and wrapped expressions are used in CSX attributes</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> enforceValidCSXAttributes: <span class="hljs-function">-></span>
|
||||
@scanTokens (token, i, tokens) ->
|
||||
<span class="hljs-keyword">if</span> token.csxColon
|
||||
next = tokens[i + <span class="hljs-number">1</span>]
|
||||
<span class="hljs-keyword">if</span> next[<span class="hljs-number">0</span>] <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">'STRING_START'</span>, <span class="hljs-string">'STRING'</span>, <span class="hljs-string">'('</span>]
|
||||
throwSyntaxError <span class="hljs-string">'expected wrapped or quoted CSX attribute'</span>, next[<span class="hljs-number">2</span>]
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-35">
|
||||
<div class="annotation">
|
||||
|
||||
@@ -965,6 +981,10 @@ blocks are added.</p>
|
||||
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> [<span class="hljs-number">1.</span><span class="hljs-number">.2</span>] <span class="hljs-keyword">when</span> @tag(i + j) <span class="hljs-keyword">in</span> [<span class="hljs-string">'OUTDENT'</span>, <span class="hljs-string">'TERMINATOR'</span>, <span class="hljs-string">'FINALLY'</span>]
|
||||
tokens.splice i + j, <span class="hljs-number">0</span>, @indentation()...
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-number">2</span> + j
|
||||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> [<span class="hljs-string">'->'</span>, <span class="hljs-string">'=>'</span>] <span class="hljs-keyword">and</span> (@tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">or</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'.'</span> <span class="hljs-keyword">and</span> token.newLine)
|
||||
[indent, outdent] = @indentation tokens[i]
|
||||
tokens.splice i + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, indent, outdent
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> SINGLE_LINERS <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'INDENT'</span> <span class="hljs-keyword">and</span>
|
||||
<span class="hljs-keyword">not</span> (tag <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'IF'</span>)
|
||||
starter = tag
|
||||
@@ -1179,7 +1199,7 @@ EXPRESSION_END = []
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre>IMPLICIT_CALL = [
|
||||
<span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-string">'PROPERTY'</span>, <span class="hljs-string">'NUMBER'</span>, <span class="hljs-string">'INFINITY'</span>, <span class="hljs-string">'NAN'</span>
|
||||
<span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-string">'CSX_TAG'</span>, <span class="hljs-string">'PROPERTY'</span>, <span class="hljs-string">'NUMBER'</span>, <span class="hljs-string">'INFINITY'</span>, <span class="hljs-string">'NAN'</span>
|
||||
<span class="hljs-string">'STRING'</span>, <span class="hljs-string">'STRING_START'</span>, <span class="hljs-string">'REGEX'</span>, <span class="hljs-string">'REGEX_START'</span>, <span class="hljs-string">'JS'</span>
|
||||
<span class="hljs-string">'NEW'</span>, <span class="hljs-string">'PARAM_START'</span>, <span class="hljs-string">'CLASS'</span>, <span class="hljs-string">'IF'</span>, <span class="hljs-string">'TRY'</span>, <span class="hljs-string">'SWITCH'</span>, <span class="hljs-string">'THIS'</span>
|
||||
<span class="hljs-string">'UNDEFINED'</span>, <span class="hljs-string">'NULL'</span>, <span class="hljs-string">'BOOL'</span>
|
||||
@@ -1254,6 +1274,21 @@ SINGLE_CLOSERS = [<span class="hljs-string">'TERMINATOR'</span>, <span class="
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-53">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-53">¶</a>
|
||||
</div>
|
||||
<p>Tokens that prevent a subsequent indent from ending implicit calls/objects</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre>CONTROL_IN_IMPLICIT = [<span class="hljs-string">'IF'</span>, <span class="hljs-string">'TRY'</span>, <span class="hljs-string">'FINALLY'</span>, <span class="hljs-string">'CATCH'</span>, <span class="hljs-string">'CLASS'</span>, <span class="hljs-string">'SWITCH'</span>]</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -553,8 +553,6 @@ textarea {
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-xs-right try-buttons">
|
||||
<button type="button" class="btn btn-primary" data-action="run-code-example" data-example="try-coffeescript" data-run="true">▶</button> 
|
||||
<button type="button" class="btn btn-primary" data-action="link" data-example="try-coffeescript"><svg aria-hidden="true" version="1.1" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -599,7 +597,7 @@ textarea {
|
||||
<a href="#conditionals" class="nav-link" data-action="sidebar-nav">If, Else, Unless, and Conditional Assignment</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#splats" class="nav-link" data-action="sidebar-nav">Splats…</a>
|
||||
<a href="#splats" class="nav-link" data-action="sidebar-nav">Splats, or Rest Parameters/Spread Syntax</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#loops" class="nav-link" data-action="sidebar-nav">Loops and Comprehensions</a>
|
||||
@@ -655,6 +653,9 @@ textarea {
|
||||
<li class="nav-item">
|
||||
<a href="#embedded" class="nav-link" data-action="sidebar-nav">Embedded JavaScript</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#jsx" class="nav-link" data-action="sidebar-nav">JSX</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
@@ -712,6 +713,9 @@ textarea {
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes" class="nav-link" data-action="sidebar-nav">Breaking Changes From 1.x</a>
|
||||
<ul class="nav">
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-change-fat-arrow" class="nav-link" data-action="sidebar-nav">Bound (Fat Arrow) Functions</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes-default-values" class="nav-link" data-action="sidebar-nav">Default Values</a>
|
||||
</li>
|
||||
@@ -724,6 +728,9 @@ textarea {
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes-super-extends" class="nav-link" data-action="sidebar-nav"><code>super</code> and <code>extends</code></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes-jsx-and-the-less-than-and-greater-than-operators" class="nav-link" data-action="sidebar-nav">JSX and the <code><</code> and <code>></code> Operators</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes-literate-coffeescript" class="nav-link" data-action="sidebar-nav">Literate CoffeeScript Parsing</a>
|
||||
</li>
|
||||
@@ -732,6 +739,9 @@ textarea {
|
||||
<li class="nav-item">
|
||||
<a href="#changelog" class="nav-link" data-action="sidebar-nav">Changelog</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/v1/" class="nav-link" data-action="sidebar-nav">Version 1.x Documentation</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -751,7 +761,7 @@ textarea {
|
||||
<section id="overview">
|
||||
<p><strong>CoffeeScript is a little language that compiles into JavaScript.</strong> Underneath that awkward Java-esque patina, JavaScript has always had a gorgeous heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.</p>
|
||||
<p>The golden rule of CoffeeScript is: <em>“It’s just JavaScript.”</em> The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly from CoffeeScript (and vice-versa). The compiled output is readable, pretty-printed, and tends to run as fast or faster than the equivalent handwritten JavaScript.</p>
|
||||
<p><strong>Latest Version:</strong> <a href="https://github.com/jashkenas/coffeescript/tarball/2.0.0-beta2">2.0.0-beta2</a></p>
|
||||
<p><strong>Latest Version:</strong> <a href="https://github.com/jashkenas/coffeescript/tarball/2.0.0-beta3">2.0.0-beta3</a></p>
|
||||
<blockquote class="uneditable-code-block"><pre><code class="language-bash">npm install -g coffeescript@next
|
||||
</code></pre>
|
||||
</blockquote>
|
||||
@@ -850,7 +860,7 @@ cubes = (function() {
|
||||
<h3>What’s New In CoffeeScript 2?</h3>
|
||||
<p>The biggest change in CoffeeScript 2 is that now the CoffeeScript compiler produces modern, ES2015+ JavaScript. A CoffeeScript <code>=></code> becomes an ES <code>=></code>, a CoffeeScript <code>class</code> becomes an ES <code>class</code> and so on. With the exception of modules (<code>import</code> and <code>export</code> statements), all the ES2015+ features that CoffeeScript supports can run natively in Node 7.6+, meaning that Node can run CoffeeScript’s output without any further processing required. You can <a href="http://coffeescript.org/v2/test.html">run the tests in your browser</a> to see if your browser can do the same; Chrome has supported all features since version 55.</p>
|
||||
<p>Support for ES2015+ syntax is important to ensure compatibility with frameworks that assume ES2015. Now that CoffeeScript compiles classes to the ES <code>class</code> keyword, it’s possible to <code>extend</code> an ES class; that wasn’t possible in CoffeeScript 1. Parity in how language features work is also important on its own; CoffeeScript “is just JavaScript,” and so things like <a href="#breaking-changes-default-values">function parameter default values</a> should behave the same in CoffeeScript as in JavaScript.</p>
|
||||
<p>Many ES2015+ features have been backported to CoffeeScript 1.11 and 1.12, including <a href="#modules">modules</a>, <a href="#generator-iteration"><code>for…of</code></a>, and <a href="#tagged-template-literals">tagged template literals</a>. One major new feature unique to CoffeeScript 2 is support for ES2017’s <a href="#async-functions">async functions</a>. More details are in the <a href="#changelog">changelog</a>.</p>
|
||||
<p>Many ES2015+ features have been backported to CoffeeScript 1.11 and 1.12, including <a href="#modules">modules</a>, <a href="#generator-iteration"><code>for…of</code></a>, and <a href="#tagged-template-literals">tagged template literals</a>. Major new features unique to CoffeeScript 2 are support for ES2017’s <a href="#async-functions">async functions</a> and for <a href="#jsx">JSX</a>. More details are in the <a href="#changelog">changelog</a>.</p>
|
||||
<p>There are very few <a href="#breaking-changes">breaking changes from CoffeeScript 1.x to 2</a>; we hope the upgrade process is smooth for most projects.</p>
|
||||
<h3>Why CoffeeScript When There’s ES2015?</h3>
|
||||
<p>CoffeeScript introduced many new features to the JavaScript world, such as <a href="#fat-arrow"><code>=></code></a> and <a href="#destructuring">destructuring</a> and <a href="#classes">classes</a>. We are happy that ECMA has seen their utility and adopted them into ECMAScript.</p>
|
||||
@@ -964,11 +974,11 @@ cubes = (function() {
|
||||
</ul>
|
||||
<h3>ES2015+ Output</h3>
|
||||
<p>CoffeeScript 2 outputs the latest ES2015+ syntax. If you’re looking for a single tool that takes CoffeeScript input and generates JavaScript output that runs in any JavaScript runtime, assuming you opt out of certain newer features, stick to <a href="/v1/">CoffeeScript 1.x</a>. CoffeeScript 2 <a href="#breaking-changes">breaks compatibility</a> with certain CoffeeScript 1.x features in order to conform with the ES2015+ specifications, and generate more idiomatic output (a CoffeeScript <code>=></code> becomes an ES <code>=></code>; a CoffeeScript <code>class</code> becomes an ES <code>class</code>; and so on).</p>
|
||||
<p>Since the CoffeeScript 2 compiler outputs ES2015+ syntax, it is your responsibility to either ensure that your target JavaScript runtime(s) support all these features, or that you pass the output through another transpiler like <a href="http://babeljs.io/">Babel</a>, <a href="https://github.com/rollup/rollup">Rollup</a> or <a href="https://github.com/google/traceur-compiler">Traceur Compiler</a>. In general, <a href="http://node.green/">CoffeeScript 2’s output is supported as is by Node.js 7.6+</a>, except for modules which require transpilation.</p>
|
||||
<p>Since the CoffeeScript 2 compiler outputs ES2015+ syntax, it is your responsibility to either ensure that your target JavaScript runtime(s) support all these features, or that you pass the output through another transpiler like <a href="http://babeljs.io/">Babel</a>, <a href="https://github.com/rollup/rollup">Rollup</a> or <a href="https://github.com/google/traceur-compiler">Traceur Compiler</a>. In general, <a href="http://node.green/">CoffeeScript 2’s output is supported as is by Node.js 7.6+</a>, except for modules and JSX which require transpilation.</p>
|
||||
<p>There are many great task runners for setting up JavaScript build chains, such as <a href="http://gulpjs.com/">Gulp</a>, <a href="https://webpack.github.io/">Webpack</a>, <a href="https://gruntjs.com/">Grunt</a> and <a href="http://broccolijs.com/">Broccoli</a>. If you’re looking for a very minimal solution to get started, you can use <a href="https://babeljs.io/docs/plugins/preset-env/">babel-preset-env</a> and the command line:</p>
|
||||
<blockquote class="uneditable-code-block"><pre><code class="language-bash">npm install --global coffeescript@next
|
||||
npm install --save-dev coffeescript@next babel-cli babel-preset-env
|
||||
coffee -p *.coffee | babel --presets env > app.js
|
||||
coffee --print *.coffee | babel --presets env > app.js
|
||||
</code></pre>
|
||||
</blockquote><h3>Node.js</h3>
|
||||
<p>If you’d like to use Node.js’ CommonJS to <code>require</code> CoffeeScript files, e.g. <code>require './app.coffee'</code>, you must first “register” CoffeeScript as an extension:</p>
|
||||
@@ -1378,8 +1388,8 @@ date = friday ? sue : jill;
|
||||
|
||||
</section>
|
||||
<section id="splats">
|
||||
<h2>Splats…</h2>
|
||||
<p>The JavaScript <code>arguments</code> object is a useful way to work with functions that accept variable numbers of arguments. CoffeeScript provides splats <code>...</code>, both for function definition as well as invocation, making variable numbers of arguments a little bit more palatable.</p>
|
||||
<h2>Splats, or Rest Parameters/Spread Syntax</h2>
|
||||
<p>The JavaScript <code>arguments</code> object is a useful way to work with functions that accept variable numbers of arguments. CoffeeScript provides splats <code>...</code>, both for function definition as well as invocation, making variable numbers of arguments a little bit more palatable. ES2015 adopted this feature as their <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">rest parameters</a>.</p>
|
||||
<aside class="code-example container-fluid bg-ribbed-dark" data-example="splats">
|
||||
<div class="row">
|
||||
<div class="col-md-6 coffeescript-input-column">
|
||||
@@ -1405,9 +1415,11 @@ contenders = [
|
||||
|
||||
awardMedals contenders...
|
||||
|
||||
alert "Gold: " + gold
|
||||
alert "Silver: " + silver
|
||||
alert "The Field: " + rest
|
||||
alert """
|
||||
Gold: #{gold}
|
||||
Silver: #{silver}
|
||||
The Field: #{rest.join ', '}
|
||||
"""
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="col-md-6 javascript-output-column">
|
||||
@@ -1425,11 +1437,7 @@ contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn
|
||||
|
||||
awardMedals(...contenders);
|
||||
|
||||
alert("Gold: " + gold);
|
||||
|
||||
alert("Silver: " + silver);
|
||||
|
||||
alert("The Field: " + rest);
|
||||
alert(`Gold: ${gold}\nSilver: ${silver}\nThe Field: ${rest.join(', ')}`);
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1441,6 +1449,71 @@ alert("The Field: " + rest);
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
<div id="array-spread" class="bookmark"></div>
|
||||
<p>Splats also let us elide array elements…</p>
|
||||
<aside class="code-example container-fluid bg-ribbed-dark" data-example="array_spread">
|
||||
<div class="row">
|
||||
<div class="col-md-6 coffeescript-input-column">
|
||||
<textarea class="coffeescript-input" id="array_spread-coffee">popular = ['pepperoni', 'sausage', 'cheese']
|
||||
unwanted = ['anchovies', 'olives']
|
||||
|
||||
all = [popular..., unwanted..., 'mushrooms']
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="col-md-6 javascript-output-column">
|
||||
<textarea class="javascript-output" id="array_spread-js">var all, popular, unwanted;
|
||||
|
||||
popular = ['pepperoni', 'sausage', 'cheese'];
|
||||
|
||||
unwanted = ['anchovies', 'olives'];
|
||||
|
||||
all = [...popular, ...unwanted, 'mushrooms'];
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-xs-right">
|
||||
<button type="button" class="btn btn-primary" data-action="run-code-example" data-example="array_spread" data-run="all"><small>▶</small> all</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
<div id="object-spread" class="bookmark"></div>
|
||||
<p>…and object properties.</p>
|
||||
<aside class="code-example container-fluid bg-ribbed-dark" data-example="object_spread">
|
||||
<div class="row">
|
||||
<div class="col-md-6 coffeescript-input-column">
|
||||
<textarea class="coffeescript-input" id="object_spread-coffee">user =
|
||||
name: 'Werner Heisenberg'
|
||||
occupation: 'theoretical physicist'
|
||||
|
||||
currentUser = { user..., status: 'Uncertain' }
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="col-md-6 javascript-output-column">
|
||||
<textarea class="javascript-output" id="object_spread-js">var currentUser, user;
|
||||
|
||||
user = {
|
||||
name: 'Werner Heisenberg',
|
||||
occupation: 'theoretical physicist'
|
||||
};
|
||||
|
||||
currentUser = Object.assign({}, user, {
|
||||
status: 'Uncertain'
|
||||
});
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-xs-right">
|
||||
<button type="button" class="btn btn-primary" data-action="run-code-example" data-example="object_spread" data-run="JSON.stringify%28currentUser%29"><small>▶</small> JSON.stringify(currentUser)</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
<p>In ECMAScript this is called <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator">spread syntax</a>, and has been supported for arrays since ES2015 but is <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Spread_in_object_literals">coming soon for objects</a>. Until object spread syntax is officially supported, the CoffeeScript compiler outputs the same polyfill as <a href="https://babeljs.io/docs/plugins/transform-object-rest-spread/">Babel’s rest spread transform</a>; but once it is supported, we will revise the compiler’s output. Note that there are <a href="https://developers.google.com/web/updates/2017/06/object-rest-spread">very subtle differences</a> between the polyfill and the current proposal.</p>
|
||||
|
||||
</section>
|
||||
<section id="loops">
|
||||
@@ -3121,6 +3194,58 @@ function time() {
|
||||
|
||||
</aside>
|
||||
|
||||
</section>
|
||||
<section id="jsx">
|
||||
<h2>JSX</h2>
|
||||
<p><a href="https://facebook.github.io/react/docs/introducing-jsx.html">JSX</a> is JavaScript containing interspersed XML elements. While conceived for <a href="https://facebook.github.io/react/">React</a>, it is not specific to any particular library or framework.</p>
|
||||
<p>CoffeeScript supports interspersed XML elements, without the need for separate plugins or special settings. The XML elements will be compiled as such, outputting JSX that could be parsed like any normal JSX file, for example by <a href="https://babeljs.io/docs/plugins/transform-react-jsx/">Babel with the React JSX transform</a>. CoffeeScript does <em>not</em> output <code>React.createElement</code> calls or any code specific to React or any other framework. It is up to you to attach another step in your build chain to convert this JSX to whatever function calls you wish the XML elements to compile to.</p>
|
||||
<p>Just like in JSX and HTML, denote XML tags using <code><</code> and <code>></code>. You can interpolate CoffeeScript code inside a tag using <code>{</code> and <code>}</code>. To avoid compiler errors, when using <code><</code> and <code>></code> to mean “less than” or “greater than,” you should wrap the operators in spaces to distinguish them from XML tags. So <code>i < len</code>, not <code>i<len</code>. The compiler tries to be forgiving when it can be sure what you intend, but always putting spaces around the “less than” and “greater than” operators will remove ambiguity.</p>
|
||||
<aside class="code-example container-fluid bg-ribbed-dark" data-example="jsx">
|
||||
<div class="row">
|
||||
<div class="col-md-6 coffeescript-input-column">
|
||||
<textarea class="coffeescript-input" id="jsx-coffee">renderStarRating = ({ rating, maxStars }) ->
|
||||
<aside title={"Rating: #{rating} of #{maxStars} stars"}>
|
||||
{for wholeStar in [0...Math.floor(rating)]
|
||||
<Star className="wholeStar" key={wholeStar} />}
|
||||
{if rating % 1 isnt 0
|
||||
<Star className="halfStar" />}
|
||||
{for emptyStar in [Math.ceil(rating)...maxStars]
|
||||
<Star className="emptyStar" key={emptyStar} />}
|
||||
</aside>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="col-md-6 javascript-output-column">
|
||||
<textarea class="javascript-output" id="jsx-js">var renderStarRating;
|
||||
|
||||
renderStarRating = function({rating, maxStars}) {
|
||||
var emptyStar, wholeStar;
|
||||
return <aside title={`Rating: ${rating} of ${maxStars} stars`}>
|
||||
{(function() {
|
||||
var i, ref, results;
|
||||
results = [];
|
||||
for (wholeStar = i = 0, ref = Math.floor(rating); 0 <= ref ? i < ref : i > ref; wholeStar = 0 <= ref ? ++i : --i) {
|
||||
results.push(<Star className="wholeStar" key={wholeStar} />);
|
||||
}
|
||||
return results;
|
||||
})()}
|
||||
{(rating % 1 !== 0 ? <Star className="halfStar" /> : void 0)}
|
||||
{(function() {
|
||||
var i, ref, ref1, results;
|
||||
results = [];
|
||||
for (emptyStar = i = ref = Math.ceil(rating), ref1 = maxStars; ref <= ref1 ? i < ref1 : i > ref1; emptyStar = ref <= ref1 ? ++i : --i) {
|
||||
results.push(<Star className="emptyStar" key={emptyStar} />);
|
||||
}
|
||||
return results;
|
||||
})()}
|
||||
</aside>;
|
||||
};
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
<p>Older plugins or forks of CoffeeScript supported JSX syntax and referred to it as CSX or CJSX. They also often used a <code>.cjsx</code> file extension, but this is no longer necessary; regalar <code>.coffee</code> will do.</p>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
<section id="literate">
|
||||
@@ -3251,7 +3376,7 @@ The CoffeeScript logo is available in SVG for use in presentations.</li>
|
||||
</section>
|
||||
<section id="annotated-source">
|
||||
<h2>Annotated Source</h2>
|
||||
<p>You can browse the CoffeeScript 2.0.0-beta2 source in readable, annotated form <a href="http://coffeescript.org/v2/annotated-source/">here</a>. You can also jump directly to a particular source file:</p>
|
||||
<p>You can browse the CoffeeScript 2.0.0-beta3 source in readable, annotated form <a href="http://coffeescript.org/v2/annotated-source/">here</a>. You can also jump directly to a particular source file:</p>
|
||||
<ul>
|
||||
<li><a href="http://coffeescript.org/v2/annotated-source/grammar.html">Grammar Rules — src/grammar</a></li>
|
||||
<li><a href="http://coffeescript.org/v2/annotated-source/lexer.html">Lexing Tokens — src/lexer</a></li>
|
||||
@@ -3357,6 +3482,44 @@ Object.defineProperty(screen, 'height', {
|
||||
<h2>Breaking Changes From CoffeeScript 1.x to 2</h2>
|
||||
<p>CoffeeScript 2 aims to output as much idiomatic ES2015+ syntax as possible with as few breaking changes from CoffeeScript 1.x as possible. Some breaking changes, unfortunately, were unavoidable.</p>
|
||||
|
||||
<section id="breaking-change-fat-arrow">
|
||||
<h3>Bound (fat arrow) functions</h3>
|
||||
<p>In CoffeeScript 1.x, <code>=></code> compiled to a regular <code>function</code> but with references to <code>this</code>/<code>@</code> rewritten to use the outer scope’s <code>this</code>, or with the inner function bound to the outer scope via <code>.bind</code> (hence the name “bound function”). In CoffeeScript 2, <code>=></code> compiles to <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">ES2015’s <code>=></code></a>, which behaves slightly differently. The largest difference is that in ES2015, <code>=></code> functions lack an <code>arguments</code> object:</p>
|
||||
<aside class="code-example container-fluid bg-ribbed-dark" data-example="breaking_change_fat_arrow">
|
||||
<div class="row">
|
||||
<div class="col-md-6 coffeescript-input-column">
|
||||
<textarea class="coffeescript-input" id="breaking_change_fat_arrow-coffee">outer = ->
|
||||
inner = => Array.from arguments
|
||||
inner()
|
||||
|
||||
outer(1, 2) # Returns '' in CoffeeScript 1.x, '1, 2' in CoffeeScript 2
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="col-md-6 javascript-output-column">
|
||||
<textarea class="javascript-output" id="breaking_change_fat_arrow-js">var outer;
|
||||
|
||||
outer = function() {
|
||||
var inner;
|
||||
inner = () => {
|
||||
return Array.from(arguments);
|
||||
};
|
||||
return inner();
|
||||
};
|
||||
|
||||
outer(1, 2);
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-xs-right">
|
||||
<button type="button" class="btn btn-primary" data-action="run-code-example" data-example="breaking_change_fat_arrow" data-run="outer%281%2C%202%29"><small>▶</small> outer(1, 2)</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
</section>
|
||||
<section id="breaking-changes-default-values">
|
||||
<h3>Default values for function parameters and destructured elements</h3>
|
||||
<p>Per the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">ES2015 spec regarding function default parameters</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Default_values">destructuring default values</a>, default values are only applied when a value is missing or <code>undefined</code>. In CoffeeScript 1.x, the default value would be applied in those cases but also if the value was <code>null</code>.</p>
|
||||
@@ -3456,13 +3619,16 @@ f = function*() {
|
||||
<blockquote class="uneditable-code-block"><pre><code class="language-coffee"><span class="class"><span class="keyword">class</span> <span class="title">B</span> <span class="keyword">extends</span> <span class="title">A</span></span>
|
||||
constructor: <span class="function">-></span> <span class="keyword">this</span> <span class="comment"># Throws a compiler error</span>
|
||||
</code></pre>
|
||||
</blockquote><p>Class methods can’t be bound (i.e. you can’t define a class method using a fat arrow) though you can define such methods in the constructor instead:</p>
|
||||
<blockquote class="uneditable-code-block"><pre><code class="language-coffee"><span class="class"><span class="keyword">class</span> <span class="title">B</span> <span class="keyword">extends</span> <span class="title">A</span></span>
|
||||
method: <span class="function">=></span> <span class="comment"># Throws a compiler error</span>
|
||||
|
||||
</blockquote><p>ES2015 classes don’t allow bound (fat arrow) methods. The CoffeeScript compiler goes through some contortions to preserve support for them, but one thing that can’t be accomodated is calling a bound method before it is bound:</p>
|
||||
<blockquote class="uneditable-code-block"><pre><code class="language-coffee"><span class="class"><span class="keyword">class</span> <span class="title">Base</span></span>
|
||||
constructor: <span class="function">-></span>
|
||||
<span class="keyword">super</span>()
|
||||
@method = <span class="function">=></span> <span class="comment"># This works</span>
|
||||
@onClick() <span class="comment"># This works</span>
|
||||
clickHandler = @onClick
|
||||
clickHandler() <span class="comment"># This throws a runtime error</span>
|
||||
|
||||
<span class="class"><span class="keyword">class</span> <span class="title">Component</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
||||
onClick: <span class="function">=></span>
|
||||
<span class="built_in">console</span>.log <span class="string">'Clicked!'</span>, @
|
||||
</code></pre>
|
||||
</blockquote><p>Class methods can’t be used with <code>new</code> (uncommon):</p>
|
||||
<blockquote class="uneditable-code-block"><pre><code class="language-coffee"><span class="class"><span class="keyword">class</span> <span class="title">Namespace</span></span>
|
||||
@@ -3619,6 +3785,11 @@ B = class B extends A {
|
||||
|
||||
</aside>
|
||||
|
||||
</section>
|
||||
<section id="breaking-changes-jsx-and-the-less-than-and-greater-than-operators">
|
||||
<h3>JSX and the <code><</code> and <code>></code> Operators</h3>
|
||||
<p>With the addition of <a href="#jsx">JSX</a>, the <code><</code> and <code>></code> characters serve as both the “less than” and “greater than” operators and as the delimiters for XML tags, like <code><div></code>. For best results, in general you should always wrap the operators in spaces to distinguish them from XML tags: <code>i < len</code>, not <code>i<len</code>. The compiler tries to be forgiving when it can be sure what you intend, but always putting spaces around the “less than” and “greater than” operators will remove ambiguity.</p>
|
||||
|
||||
</section>
|
||||
<section id="breaking-changes-literate-coffeescript">
|
||||
<h3>Literate CoffeeScript parsing</h3>
|
||||
@@ -3630,6 +3801,17 @@ B = class B extends A {
|
||||
</section>
|
||||
<section id="changelog">
|
||||
<h2>Changelog</h2>
|
||||
<div class="anchor" id="2.0.0-beta3"></div>
|
||||
<h2 class="header">
|
||||
<a href="https://github.com/jashkenas/coffeescript/compare/2.0.0-beta2...2.0.0-beta3">2.0.0-beta3</a>
|
||||
<span class="timestamp"> — <time datetime="2017-06-30">June 30, 2017</time></span>
|
||||
</h2><ul>
|
||||
<li><a href="#jsx">JSX</a> is now supported.</li>
|
||||
<li><a href="#object-spread">Object rest/spread properties</a> are now supported.</li>
|
||||
<li>Bound (fat arrow) methods are once again supported in classes; though an error will be thrown if you attempt to call the method before it is bound. See <a href="#breaking-changes-classes">breaking changes for classes</a>.</li>
|
||||
<li>The REPL no longer warns about assigning to <code>_</code>.</li>
|
||||
<li>Bugfixes for destructured nested default values and issues related to chaining or continuing expressions across multiple lines.</li>
|
||||
</ul>
|
||||
<div class="anchor" id="2.0.0-beta2"></div>
|
||||
<h2 class="header">
|
||||
<a href="https://github.com/jashkenas/coffeescript/compare/2.0.0-beta1...2.0.0-beta2">2.0.0-beta2</a>
|
||||
@@ -4270,17 +4452,6 @@ $(document).ready ->
|
||||
window.location = event.target.href
|
||||
, 260 # Wait for the sidebar to slide away before navigating
|
||||
|
||||
|
||||
# Try CoffeeScript
|
||||
toggleTry = ->
|
||||
$('#try, #try-link').toggleClass 'active'
|
||||
closeTry = ->
|
||||
$('#try, #try-link').removeClass 'active'
|
||||
|
||||
$('[data-toggle="try"]').click toggleTry
|
||||
$('[data-close="try"]').click closeTry
|
||||
|
||||
|
||||
# Initialize Scrollspy for sidebar navigation; http://v4-alpha.getbootstrap.com/components/scrollspy/
|
||||
# See also http://www.codingeverything.com/2014/02/BootstrapDocsSideBar.html and http://jsfiddle.net/KyleMit/v6zhz/
|
||||
$('body').scrollspy
|
||||
@@ -4317,6 +4488,7 @@ $(document).ready ->
|
||||
viewportMargin: Infinity
|
||||
|
||||
# Whenever the user edits the CoffeeScript side of a code example, update the JavaScript output
|
||||
# If the editor is Try CoffeeScript, also update the hash and save this code in localStorage
|
||||
if mode is 'coffeescript'
|
||||
pending = null
|
||||
editor.on 'change', (instance, change) ->
|
||||
@@ -4324,13 +4496,38 @@ $(document).ready ->
|
||||
pending = setTimeout ->
|
||||
lastCompilationStartTime = Date.now()
|
||||
try
|
||||
output = CoffeeScript.compile editor.getValue(), bare: yes
|
||||
coffee = editor.getValue()
|
||||
if index is 0 and $('#try').hasClass('active') # If this is the editor in Try CoffeeScript and it’s still visible
|
||||
# Update the hash with the current code
|
||||
link = "try:#{encodeURIComponent coffee}"
|
||||
window.history.pushState {}, 'CoffeeScript', "#{location.href.split('#')[0]}##{link}"
|
||||
# Save this to the user’s localStorage
|
||||
try
|
||||
if window.localStorage?
|
||||
window.localStorage.setItem 'tryCoffeeScriptCode', coffee
|
||||
catch exception
|
||||
output = CoffeeScript.compile coffee, bare: yes
|
||||
lastCompilationElapsedTime = Math.max(200, Date.now() - lastCompilationStartTime)
|
||||
catch exception
|
||||
output = "#{exception}"
|
||||
editors[index + 1].setValue output
|
||||
, lastCompilationElapsedTime
|
||||
|
||||
# Fix the code editors’ handling of tab-indented code
|
||||
editor.addKeyMap
|
||||
'Tab': (cm) ->
|
||||
if cm.somethingSelected()
|
||||
cm.indentSelection 'add'
|
||||
else if /^\t/m.test cm.getValue()
|
||||
# If any lines start with a tab, treat this as tab-indented code
|
||||
cm.execCommand 'insertTab'
|
||||
else
|
||||
cm.execCommand 'insertSoftTab'
|
||||
'Shift-Tab': (cm) ->
|
||||
cm.indentSelection 'subtract'
|
||||
'Enter': (cm) ->
|
||||
cm.options.indentWithTabs = /^\t/m.test cm.getValue()
|
||||
cm.execCommand 'newlineAndIndent'
|
||||
|
||||
# Handle the code example buttons
|
||||
$('[data-action="run-code-example"]').click ->
|
||||
@@ -4340,22 +4537,34 @@ $(document).ready ->
|
||||
js = "#{js}\nalert(#{unescape run});" unless run is yes
|
||||
eval js
|
||||
|
||||
$('[data-action="link"]').click ->
|
||||
index = $("##{$(@).data('example')}-coffee").data 'index'
|
||||
coffee = editors[index].getValue()
|
||||
link = "try:#{encodeURIComponent coffee}"
|
||||
window.history.pushState {}, 'CoffeeScript', "#{location.href.split('#')[0]}##{link}"
|
||||
|
||||
# Try CoffeeScript
|
||||
toggleTry = (checkLocalStorage = no) ->
|
||||
if checkLocalStorage and window.localStorage?
|
||||
try
|
||||
coffee = window.localStorage.getItem 'tryCoffeeScriptCode'
|
||||
if coffee?
|
||||
editors[0].setValue coffee
|
||||
catch exception
|
||||
$('#try, #try-link').toggleClass 'active'
|
||||
closeTry = ->
|
||||
$('#try, #try-link').removeClass 'active'
|
||||
|
||||
$('[data-toggle="try"]').click toggleTry
|
||||
$('[data-close="try"]').click closeTry
|
||||
|
||||
|
||||
# Configure the initial state
|
||||
if window.location.hash?
|
||||
if window.location.hash is '#try'
|
||||
toggleTry()
|
||||
toggleTry yes
|
||||
else if window.location.hash.indexOf('#try') is 0
|
||||
editors[0].setValue decodeURIComponent window.location.hash[5..]
|
||||
toggleTry()
|
||||
else
|
||||
initializeScrollspyFromHash window.location.hash
|
||||
# Initializing the code editors might’ve thrown off our vertical scroll position
|
||||
document.getElementById(window.location.hash.slice(1)).scrollIntoView()
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
2161
docs/v2/test.html
2161
docs/v2/test.html
File diff suppressed because it is too large
Load Diff
4
documentation/examples/array_spread.coffee
Normal file
4
documentation/examples/array_spread.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
popular = ['pepperoni', 'sausage', 'cheese']
|
||||
unwanted = ['anchovies', 'olives']
|
||||
|
||||
all = [popular..., unwanted..., 'mushrooms']
|
||||
5
documentation/examples/breaking_change_fat_arrow.coffee
Normal file
5
documentation/examples/breaking_change_fat_arrow.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
outer = ->
|
||||
inner = => Array.from arguments
|
||||
inner()
|
||||
|
||||
outer(1, 2) # Returns '' in CoffeeScript 1.x, '1, 2' in CoffeeScript 2
|
||||
9
documentation/examples/jsx.coffee
Normal file
9
documentation/examples/jsx.coffee
Normal file
@@ -0,0 +1,9 @@
|
||||
renderStarRating = ({ rating, maxStars }) ->
|
||||
<aside title={"Rating: #{rating} of #{maxStars} stars"}>
|
||||
{for wholeStar in [0...Math.floor(rating)]
|
||||
<Star className="wholeStar" key={wholeStar} />}
|
||||
{if rating % 1 isnt 0
|
||||
<Star className="halfStar" />}
|
||||
{for emptyStar in [Math.ceil(rating)...maxStars]
|
||||
<Star className="emptyStar" key={emptyStar} />}
|
||||
</aside>
|
||||
5
documentation/examples/object_spread.coffee
Normal file
5
documentation/examples/object_spread.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
user =
|
||||
name: 'Werner Heisenberg'
|
||||
occupation: 'theoretical physicist'
|
||||
|
||||
currentUser = { user..., status: 'Uncertain' }
|
||||
@@ -20,6 +20,8 @@ contenders = [
|
||||
|
||||
awardMedals contenders...
|
||||
|
||||
alert "Gold: " + gold
|
||||
alert "Silver: " + silver
|
||||
alert "The Field: " + rest
|
||||
alert """
|
||||
Gold: #{gold}
|
||||
Silver: #{silver}
|
||||
The Field: #{rest.join ', '}
|
||||
"""
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg aria-hidden="true" version="1.1" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 436 B |
7
documentation/sections/breaking_change_fat_arrow.md
Normal file
7
documentation/sections/breaking_change_fat_arrow.md
Normal file
@@ -0,0 +1,7 @@
|
||||
### Bound (fat arrow) functions
|
||||
|
||||
In CoffeeScript 1.x, `=>` compiled to a regular `function` but with references to `this`/`@` rewritten to use the outer scope’s `this`, or with the inner function bound to the outer scope via `.bind` (hence the name “bound function”). In CoffeeScript 2, `=>` compiles to [ES2015’s `=>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), which behaves slightly differently. The largest difference is that in ES2015, `=>` functions lack an `arguments` object:
|
||||
|
||||
```
|
||||
codeFor('breaking_change_fat_arrow', 'outer(1, 2)')
|
||||
```
|
||||
@@ -16,15 +16,18 @@ class B extends A
|
||||
constructor: -> this # Throws a compiler error
|
||||
```
|
||||
|
||||
Class methods can’t be bound (i.e. you can’t define a class method using a fat arrow) though you can define such methods in the constructor instead:
|
||||
ES2015 classes don’t allow bound (fat arrow) methods. The CoffeeScript compiler goes through some contortions to preserve support for them, but one thing that can’t be accomodated is calling a bound method before it is bound:
|
||||
|
||||
```coffee
|
||||
class B extends A
|
||||
method: => # Throws a compiler error
|
||||
|
||||
class Base
|
||||
constructor: ->
|
||||
super()
|
||||
@method = => # This works
|
||||
@onClick() # This works
|
||||
clickHandler = @onClick
|
||||
clickHandler() # This throws a runtime error
|
||||
|
||||
class Component extends Base
|
||||
onClick: =>
|
||||
console.log 'Clicked!', @
|
||||
```
|
||||
|
||||
Class methods can’t be used with `new` (uncommon):
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
### JSX and the `<` and `>` Operators
|
||||
|
||||
With the addition of [JSX](#jsx), the `<` and `>` characters serve as both the “less than” and “greater than” operators and as the delimiters for XML tags, like `<div>`. For best results, in general you should always wrap the operators in spaces to distinguish them from XML tags: `i < len`, not `i<len`. The compiler tries to be forgiving when it can be sure what you intend, but always putting spaces around the “less than” and “greater than” operators will remove ambiguity.
|
||||
@@ -1,5 +1,16 @@
|
||||
## Changelog
|
||||
|
||||
```
|
||||
releaseHeader('2017-06-30', '2.0.0-beta3', '2.0.0-beta2')
|
||||
```
|
||||
|
||||
* [JSX](#jsx) is now supported.
|
||||
* [Object rest/spread properties](#object-spread) are now supported.
|
||||
* Bound (fat arrow) methods are once again supported in classes; though an error will be thrown if you attempt to call the method before it is bound. See [breaking changes for classes](#breaking-changes-classes).
|
||||
* The REPL no longer warns about assigning to `_`.
|
||||
* Bugfixes for destructured nested default values and issues related to chaining or continuing expressions across multiple lines.
|
||||
|
||||
|
||||
```
|
||||
releaseHeader('2017-05-16', '2.0.0-beta2', '2.0.0-beta1')
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ The biggest change in CoffeeScript 2 is that now the CoffeeScript compiler produ
|
||||
|
||||
Support for ES2015+ syntax is important to ensure compatibility with frameworks that assume ES2015. Now that CoffeeScript compiles classes to the ES `class` keyword, it’s possible to `extend` an ES class; that wasn’t possible in CoffeeScript 1. Parity in how language features work is also important on its own; CoffeeScript “is just JavaScript,” and so things like [function parameter default values](#breaking-changes-default-values) should behave the same in CoffeeScript as in JavaScript.
|
||||
|
||||
Many ES2015+ features have been backported to CoffeeScript 1.11 and 1.12, including [modules](#modules), [`for…of`](#generator-iteration), and [tagged template literals](#tagged-template-literals). One major new feature unique to CoffeeScript 2 is support for ES2017’s [async functions](#async-functions). More details are in the [changelog](#changelog).
|
||||
Many ES2015+ features have been backported to CoffeeScript 1.11 and 1.12, including [modules](#modules), [`for…of`](#generator-iteration), and [tagged template literals](#tagged-template-literals). Major new features unique to CoffeeScript 2 are support for ES2017’s [async functions](#async-functions) and for [JSX](#jsx). More details are in the [changelog](#changelog).
|
||||
|
||||
There are very few [breaking changes from CoffeeScript 1.x to 2](#breaking-changes); we hope the upgrade process is smooth for most projects.
|
||||
|
||||
|
||||
13
documentation/sections/jsx.md
Normal file
13
documentation/sections/jsx.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## JSX
|
||||
|
||||
[JSX](https://facebook.github.io/react/docs/introducing-jsx.html) is JavaScript containing interspersed XML elements. While conceived for [React](https://facebook.github.io/react/), it is not specific to any particular library or framework.
|
||||
|
||||
CoffeeScript supports interspersed XML elements, without the need for separate plugins or special settings. The XML elements will be compiled as such, outputting JSX that could be parsed like any normal JSX file, for example by [Babel with the React JSX transform](https://babeljs.io/docs/plugins/transform-react-jsx/). CoffeeScript does _not_ output `React.createElement` calls or any code specific to React or any other framework. It is up to you to attach another step in your build chain to convert this JSX to whatever function calls you wish the XML elements to compile to.
|
||||
|
||||
Just like in JSX and HTML, denote XML tags using `<` and `>`. You can interpolate CoffeeScript code inside a tag using `{` and `}`. To avoid compiler errors, when using `<` and `>` to mean “less than” or “greater than,” you should wrap the operators in spaces to distinguish them from XML tags. So `i < len`, not `i<len`. The compiler tries to be forgiving when it can be sure what you intend, but always putting spaces around the “less than” and “greater than” operators will remove ambiguity.
|
||||
|
||||
```
|
||||
codeFor('jsx')
|
||||
```
|
||||
|
||||
Older plugins or forks of CoffeeScript supported JSX syntax and referred to it as CSX or CJSX. They also often used a `.cjsx` file extension, but this is no longer necessary; regalar `.coffee` will do.
|
||||
@@ -1,7 +1,25 @@
|
||||
## Splats…
|
||||
## Splats, or Rest Parameters/Spread Syntax
|
||||
|
||||
The JavaScript `arguments` object is a useful way to work with functions that accept variable numbers of arguments. CoffeeScript provides splats `...`, both for function definition as well as invocation, making variable numbers of arguments a little bit more palatable.
|
||||
The JavaScript `arguments` object is a useful way to work with functions that accept variable numbers of arguments. CoffeeScript provides splats `...`, both for function definition as well as invocation, making variable numbers of arguments a little bit more palatable. ES2015 adopted this feature as their [rest parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
|
||||
|
||||
```
|
||||
codeFor('splats', true)
|
||||
```
|
||||
|
||||
<div id="array-spread" class="bookmark"></div>
|
||||
|
||||
Splats also let us elide array elements...
|
||||
|
||||
```
|
||||
codeFor('array_spread', 'all')
|
||||
```
|
||||
|
||||
<div id="object-spread" class="bookmark"></div>
|
||||
|
||||
...and object properties.
|
||||
|
||||
```
|
||||
codeFor('object_spread', 'JSON.stringify(currentUser)')
|
||||
```
|
||||
|
||||
In ECMAScript this is called [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator), and has been supported for arrays since ES2015 but is [coming soon for objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Spread_in_object_literals). Until object spread syntax is officially supported, the CoffeeScript compiler outputs the same polyfill as [Babel’s rest spread transform](https://babeljs.io/docs/plugins/transform-object-rest-spread/); but once it is supported, we will revise the compiler’s output. Note that there are [very subtle differences](https://developers.google.com/web/updates/2017/06/object-rest-spread) between the polyfill and the current proposal.
|
||||
@@ -40,14 +40,14 @@ Once installed, you should have access to the `coffee` command, which can execut
|
||||
|
||||
CoffeeScript 2 outputs the latest ES2015+ syntax. If you’re looking for a single tool that takes CoffeeScript input and generates JavaScript output that runs in any JavaScript runtime, assuming you opt out of certain newer features, stick to [CoffeeScript 1.x](/v1/). CoffeeScript 2 [breaks compatibility](#breaking-changes) with certain CoffeeScript 1.x features in order to conform with the ES2015+ specifications, and generate more idiomatic output (a CoffeeScript `=>` becomes an ES `=>`; a CoffeeScript `class` becomes an ES `class`; and so on).
|
||||
|
||||
Since the CoffeeScript 2 compiler outputs ES2015+ syntax, it is your responsibility to either ensure that your target JavaScript runtime(s) support all these features, or that you pass the output through another transpiler like [Babel](http://babeljs.io/), [Rollup](https://github.com/rollup/rollup) or [Traceur Compiler](https://github.com/google/traceur-compiler). In general, [CoffeeScript 2’s output is supported as is by Node.js 7.6+](http://node.green/), except for modules which require transpilation.
|
||||
Since the CoffeeScript 2 compiler outputs ES2015+ syntax, it is your responsibility to either ensure that your target JavaScript runtime(s) support all these features, or that you pass the output through another transpiler like [Babel](http://babeljs.io/), [Rollup](https://github.com/rollup/rollup) or [Traceur Compiler](https://github.com/google/traceur-compiler). In general, [CoffeeScript 2’s output is supported as is by Node.js 7.6+](http://node.green/), except for modules and JSX which require transpilation.
|
||||
|
||||
There are many great task runners for setting up JavaScript build chains, such as [Gulp](http://gulpjs.com/), [Webpack](https://webpack.github.io/), [Grunt](https://gruntjs.com/) and [Broccoli](http://broccolijs.com/). If you’re looking for a very minimal solution to get started, you can use [babel-preset-env](https://babeljs.io/docs/plugins/preset-env/) and the command line:
|
||||
|
||||
```bash
|
||||
npm install --global coffeescript@next
|
||||
npm install --save-dev coffeescript@next babel-cli babel-preset-env
|
||||
coffee -p *.coffee | babel --presets env > app.js
|
||||
coffee --print *.coffee | babel --presets env > app.js
|
||||
```
|
||||
|
||||
### Node.js
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<script type="text/coffeescript">
|
||||
@testingBrowser = yes
|
||||
@global = window
|
||||
bold = red = green = reset = ''
|
||||
stdout = document.getElementById 'stdout'
|
||||
start = new Date
|
||||
@currentFile = ''
|
||||
|
||||
@@ -106,6 +106,9 @@
|
||||
<section id="embedded">
|
||||
<%= htmlFor('embedded') %>
|
||||
</section>
|
||||
<section id="jsx">
|
||||
<%= htmlFor('jsx') %>
|
||||
</section>
|
||||
</section>
|
||||
<section id="literate">
|
||||
<%= htmlFor('literate') %>
|
||||
@@ -154,6 +157,9 @@
|
||||
</section>
|
||||
<section id="breaking-changes">
|
||||
<%= htmlFor('breaking_changes') %>
|
||||
<section id="breaking-change-fat-arrow">
|
||||
<%= htmlFor('breaking_change_fat_arrow') %>
|
||||
</section>
|
||||
<section id="breaking-changes-default-values">
|
||||
<%= htmlFor('breaking_changes_default_values') %>
|
||||
</section>
|
||||
@@ -166,6 +172,9 @@
|
||||
<section id="breaking-changes-super-extends">
|
||||
<%= htmlFor('breaking_changes_super_extends') %>
|
||||
</section>
|
||||
<section id="breaking-changes-jsx-and-the-less-than-and-greater-than-operators">
|
||||
<%= htmlFor('breaking_changes_jsx_and_the_less_than_and_greater_than_operators') %>
|
||||
</section>
|
||||
<section id="breaking-changes-literate-coffeescript">
|
||||
<%= htmlFor('breaking_changes_literate_coffeescript') %>
|
||||
</section>
|
||||
|
||||
@@ -13,17 +13,6 @@ $(document).ready ->
|
||||
window.location = event.target.href
|
||||
, 260 # Wait for the sidebar to slide away before navigating
|
||||
|
||||
|
||||
# Try CoffeeScript
|
||||
toggleTry = ->
|
||||
$('#try, #try-link').toggleClass 'active'
|
||||
closeTry = ->
|
||||
$('#try, #try-link').removeClass 'active'
|
||||
|
||||
$('[data-toggle="try"]').click toggleTry
|
||||
$('[data-close="try"]').click closeTry
|
||||
|
||||
|
||||
# Initialize Scrollspy for sidebar navigation; http://v4-alpha.getbootstrap.com/components/scrollspy/
|
||||
# See also http://www.codingeverything.com/2014/02/BootstrapDocsSideBar.html and http://jsfiddle.net/KyleMit/v6zhz/
|
||||
$('body').scrollspy
|
||||
@@ -60,6 +49,7 @@ $(document).ready ->
|
||||
viewportMargin: Infinity
|
||||
|
||||
# Whenever the user edits the CoffeeScript side of a code example, update the JavaScript output
|
||||
# If the editor is Try CoffeeScript, also update the hash and save this code in localStorage
|
||||
if mode is 'coffeescript'
|
||||
pending = null
|
||||
editor.on 'change', (instance, change) ->
|
||||
@@ -67,13 +57,38 @@ $(document).ready ->
|
||||
pending = setTimeout ->
|
||||
lastCompilationStartTime = Date.now()
|
||||
try
|
||||
output = CoffeeScript.compile editor.getValue(), bare: yes
|
||||
coffee = editor.getValue()
|
||||
if index is 0 and $('#try').hasClass('active') # If this is the editor in Try CoffeeScript and it’s still visible
|
||||
# Update the hash with the current code
|
||||
link = "try:#{encodeURIComponent coffee}"
|
||||
window.history.pushState {}, 'CoffeeScript', "#{location.href.split('#')[0]}##{link}"
|
||||
# Save this to the user’s localStorage
|
||||
try
|
||||
if window.localStorage?
|
||||
window.localStorage.setItem 'tryCoffeeScriptCode', coffee
|
||||
catch exception
|
||||
output = CoffeeScript.compile coffee, bare: yes
|
||||
lastCompilationElapsedTime = Math.max(200, Date.now() - lastCompilationStartTime)
|
||||
catch exception
|
||||
output = "#{exception}"
|
||||
editors[index + 1].setValue output
|
||||
, lastCompilationElapsedTime
|
||||
|
||||
# Fix the code editors’ handling of tab-indented code
|
||||
editor.addKeyMap
|
||||
'Tab': (cm) ->
|
||||
if cm.somethingSelected()
|
||||
cm.indentSelection 'add'
|
||||
else if /^\t/m.test cm.getValue()
|
||||
# If any lines start with a tab, treat this as tab-indented code
|
||||
cm.execCommand 'insertTab'
|
||||
else
|
||||
cm.execCommand 'insertSoftTab'
|
||||
'Shift-Tab': (cm) ->
|
||||
cm.indentSelection 'subtract'
|
||||
'Enter': (cm) ->
|
||||
cm.options.indentWithTabs = /^\t/m.test cm.getValue()
|
||||
cm.execCommand 'newlineAndIndent'
|
||||
|
||||
# Handle the code example buttons
|
||||
$('[data-action="run-code-example"]').click ->
|
||||
@@ -83,19 +98,31 @@ $(document).ready ->
|
||||
js = "#{js}\nalert(#{unescape run});" unless run is yes
|
||||
eval js
|
||||
|
||||
$('[data-action="link"]').click ->
|
||||
index = $("##{$(@).data('example')}-coffee").data 'index'
|
||||
coffee = editors[index].getValue()
|
||||
link = "try:#{encodeURIComponent coffee}"
|
||||
window.history.pushState {}, 'CoffeeScript', "#{location.href.split('#')[0]}##{link}"
|
||||
|
||||
# Try CoffeeScript
|
||||
toggleTry = (checkLocalStorage = no) ->
|
||||
if checkLocalStorage and window.localStorage?
|
||||
try
|
||||
coffee = window.localStorage.getItem 'tryCoffeeScriptCode'
|
||||
if coffee?
|
||||
editors[0].setValue coffee
|
||||
catch exception
|
||||
$('#try, #try-link').toggleClass 'active'
|
||||
closeTry = ->
|
||||
$('#try, #try-link').removeClass 'active'
|
||||
|
||||
$('[data-toggle="try"]').click toggleTry
|
||||
$('[data-close="try"]').click closeTry
|
||||
|
||||
|
||||
# Configure the initial state
|
||||
if window.location.hash?
|
||||
if window.location.hash is '#try'
|
||||
toggleTry()
|
||||
toggleTry yes
|
||||
else if window.location.hash.indexOf('#try') is 0
|
||||
editors[0].setValue decodeURIComponent window.location.hash[5..]
|
||||
toggleTry()
|
||||
else
|
||||
initializeScrollspyFromHash window.location.hash
|
||||
# Initializing the code editors might’ve thrown off our vertical scroll position
|
||||
document.getElementById(window.location.hash.slice(1)).scrollIntoView()
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<a href="#conditionals" class="nav-link" data-action="sidebar-nav">If, Else, Unless, and Conditional Assignment</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#splats" class="nav-link" data-action="sidebar-nav">Splats…</a>
|
||||
<a href="#splats" class="nav-link" data-action="sidebar-nav">Splats, or Rest Parameters/Spread Syntax</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#loops" class="nav-link" data-action="sidebar-nav">Loops and Comprehensions</a>
|
||||
@@ -90,6 +90,9 @@
|
||||
<li class="nav-item">
|
||||
<a href="#embedded" class="nav-link" data-action="sidebar-nav">Embedded JavaScript</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#jsx" class="nav-link" data-action="sidebar-nav">JSX</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
@@ -147,6 +150,9 @@
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes" class="nav-link" data-action="sidebar-nav">Breaking Changes From 1.x</a>
|
||||
<ul class="nav">
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-change-fat-arrow" class="nav-link" data-action="sidebar-nav">Bound (Fat Arrow) Functions</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes-default-values" class="nav-link" data-action="sidebar-nav">Default Values</a>
|
||||
</li>
|
||||
@@ -159,6 +165,9 @@
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes-super-extends" class="nav-link" data-action="sidebar-nav"><code>super</code> and <code>extends</code></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes-jsx-and-the-less-than-and-greater-than-operators" class="nav-link" data-action="sidebar-nav">JSX and the <code><</code> and <code>></code> Operators</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#breaking-changes-literate-coffeescript" class="nav-link" data-action="sidebar-nav">Literate CoffeeScript Parsing</a>
|
||||
</li>
|
||||
@@ -167,5 +176,8 @@
|
||||
<li class="nav-item">
|
||||
<a href="#changelog" class="nav-link" data-action="sidebar-nav">Changelog</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/v1/" class="nav-link" data-action="sidebar-nav">Version 1.x Documentation</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-xs-right try-buttons">
|
||||
<button type="button" class="btn btn-primary" data-action="run-code-example" data-example="try-coffeescript" data-run="true">▶</button> 
|
||||
<button type="button" class="btn btn-primary" data-action="link" data-example="try-coffeescript"><%= include('documentation/images/link.svg') %></button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var CoffeeScript, compile, runScripts,
|
||||
indexOf = [].indexOf;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var Lexer, SourceMap, base64encode, compile, formatSourcePosition, getSourceMap, helpers, lexer, packageJson, parser, sourceMaps, sources, withPrettyErrors;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs,
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, buildCSOptionParser, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs,
|
||||
indexOf = [].indexOf;
|
||||
|
||||
fs = require('fs');
|
||||
@@ -49,8 +49,13 @@
|
||||
|
||||
optionParser = null;
|
||||
|
||||
exports.buildCSOptionParser = buildCSOptionParser = function() {
|
||||
return new optparse.OptionParser(SWITCHES, BANNER);
|
||||
};
|
||||
|
||||
exports.run = function() {
|
||||
var i, len, literals, ref, replCliOpts, results, source;
|
||||
optionParser = buildCSOptionParser();
|
||||
parseOptions();
|
||||
replCliOpts = {
|
||||
useGlobal: true
|
||||
@@ -532,7 +537,6 @@
|
||||
|
||||
parseOptions = function() {
|
||||
var o;
|
||||
optionParser = new optparse.OptionParser(SWITCHES, BANNER);
|
||||
o = opts = optionParser.parse(process.argv.slice(2));
|
||||
o.compile || (o.compile = !!o.output);
|
||||
o.run = !(o.compile || o.print || o.map);
|
||||
@@ -587,7 +591,7 @@
|
||||
};
|
||||
|
||||
usage = function() {
|
||||
return printLine((new optparse.OptionParser(SWITCHES, BANNER)).help());
|
||||
return printLine(optionParser.help());
|
||||
};
|
||||
|
||||
version = function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
Identifier: [
|
||||
o('IDENTIFIER', function() {
|
||||
return new IdentifierLiteral($1);
|
||||
}), o('CSX_TAG', function() {
|
||||
return new CSXTag($1);
|
||||
})
|
||||
],
|
||||
Property: [
|
||||
@@ -121,7 +123,7 @@
|
||||
AssignObj: [
|
||||
o('ObjAssignable', function() {
|
||||
return new Value($1);
|
||||
}), o('ObjAssignable : Expression', function() {
|
||||
}), o('ObjRestValue'), o('ObjAssignable : Expression', function() {
|
||||
return new Assign(LOC(1)(new Value($1)), $3, 'object', {
|
||||
operatorToken: LOC(2)(new Literal($2))
|
||||
});
|
||||
@@ -141,6 +143,29 @@
|
||||
],
|
||||
SimpleObjAssignable: [o('Identifier'), o('Property'), o('ThisProperty')],
|
||||
ObjAssignable: [o('SimpleObjAssignable'), o('AlphaNumeric')],
|
||||
ObjRestValue: [
|
||||
o('SimpleObjAssignable ...', function() {
|
||||
return new Splat(new Value($1));
|
||||
}), o('ObjSpreadExpr ...', function() {
|
||||
return new Splat($1);
|
||||
})
|
||||
],
|
||||
ObjSpreadExpr: [
|
||||
o('ObjSpreadIdentifier'), o('Object'), o('Parenthetical'), o('Super'), o('This'), o('SUPER Arguments', function() {
|
||||
return new SuperCall(LOC(1)(new Super), $2);
|
||||
}), o('SimpleObjAssignable Arguments', function() {
|
||||
return new Call(new Value($1), $2);
|
||||
}), o('ObjSpreadExpr Arguments', function() {
|
||||
return new Call($1, $2);
|
||||
})
|
||||
],
|
||||
ObjSpreadIdentifier: [
|
||||
o('SimpleObjAssignable . Property', function() {
|
||||
return (new Value($1)).add(new Access($3));
|
||||
}), o('SimpleObjAssignable INDEX_START IndexValue INDEX_END', function() {
|
||||
return (new Value($1)).add($3);
|
||||
})
|
||||
],
|
||||
Return: [
|
||||
o('RETURN Expression', function() {
|
||||
return new Return($2);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var buildLocationData, extend, flatten, ref, repeat, syntaxErrorToString;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var CoffeeScript, compile, ext, fn, fs, helpers, i, len, path, ref, vm,
|
||||
hasProp = {}.hasOwnProperty;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, REGEX_INVALID_ESCAPE, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_INVALID_ESCAPE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, UNICODE_CODE_POINT_ESCAPE, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, merge, repeat, starts, throwSyntaxError,
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARABLE_LEFT_SIDE, COMPARE, COMPOUND_ASSIGN, CSX_ATTRIBUTE, CSX_IDENTIFIER, CSX_INTERPOLATION, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INSIDE_CSX, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, REGEX_INVALID_ESCAPE, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_INVALID_ESCAPE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, UNICODE_CODE_POINT_ESCAPE, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, merge, repeat, starts, throwSyntaxError,
|
||||
indexOf = [].indexOf;
|
||||
|
||||
({Rewriter, INVERSES} = require('./rewriter'));
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
exports.Lexer = Lexer = class Lexer {
|
||||
tokenize(code, opts = {}) {
|
||||
var consumed, end, i;
|
||||
var consumed, end, i, ref;
|
||||
this.literate = opts.literate;
|
||||
this.indent = 0;
|
||||
this.baseIndent = 0;
|
||||
@@ -24,12 +24,13 @@
|
||||
this.seenExport = false;
|
||||
this.importSpecifierList = false;
|
||||
this.exportSpecifierList = false;
|
||||
this.csxDepth = 0;
|
||||
this.chunkLine = opts.line || 0;
|
||||
this.chunkColumn = opts.column || 0;
|
||||
code = this.clean(code);
|
||||
i = 0;
|
||||
while (this.chunk = code.slice(i)) {
|
||||
consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken();
|
||||
consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.stringToken() || this.numberToken() || this.csxToken() || this.regexToken() || this.jsToken() || this.literalToken();
|
||||
[this.chunkLine, this.chunkColumn] = this.getLineAndColumnFromChunk(consumed);
|
||||
i += consumed;
|
||||
if (opts.untilBalanced && this.ends.length === 0) {
|
||||
@@ -41,7 +42,7 @@
|
||||
}
|
||||
this.closeIndentation();
|
||||
if (end = this.ends.pop()) {
|
||||
this.error(`missing ${end.tag}`, end.origin[2]);
|
||||
this.error(`missing ${end.tag}`, ((ref = end.origin) != null ? ref : end)[2]);
|
||||
}
|
||||
if (opts.rewrite === false) {
|
||||
return this.tokens;
|
||||
@@ -65,8 +66,10 @@
|
||||
}
|
||||
|
||||
identifierToken() {
|
||||
var alias, colon, colonOffset, id, idLength, input, match, poppedToken, prev, prevprev, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, tag, tagToken;
|
||||
if (!(match = IDENTIFIER.exec(this.chunk))) {
|
||||
var alias, colon, colonOffset, colonToken, id, idLength, inCSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, regex, tag, tagToken;
|
||||
inCSXTag = this.atCSXTag();
|
||||
regex = inCSXTag ? CSX_ATTRIBUTE : IDENTIFIER;
|
||||
if (!(match = regex.exec(this.chunk))) {
|
||||
return 0;
|
||||
}
|
||||
[input, id, colon] = match;
|
||||
@@ -180,8 +183,14 @@
|
||||
[tagToken[2].first_line, tagToken[2].first_column] = [poppedToken[2].first_line, poppedToken[2].first_column];
|
||||
}
|
||||
if (colon) {
|
||||
colonOffset = input.lastIndexOf(':');
|
||||
this.token(':', ':', colonOffset, colon.length);
|
||||
colonOffset = input.lastIndexOf(inCSXTag ? '=' : ':');
|
||||
colonToken = this.token(':', ':', colonOffset, colon.length);
|
||||
if (inCSXTag) {
|
||||
colonToken.csxColon = true;
|
||||
}
|
||||
}
|
||||
if (inCSXTag && tag === 'IDENTIFIER' && prev[0] !== ':') {
|
||||
this.token(',', ',', 0, 0, tagToken);
|
||||
}
|
||||
return input.length;
|
||||
}
|
||||
@@ -313,6 +322,9 @@
|
||||
return value;
|
||||
});
|
||||
}
|
||||
if (this.atCSXTag()) {
|
||||
this.token(',', ',', 0, 0, this.prev);
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
@@ -501,7 +513,7 @@
|
||||
while (moveOut > 0) {
|
||||
lastIndent = this.indents[this.indents.length - 1];
|
||||
if (!lastIndent) {
|
||||
moveOut = 0;
|
||||
this.outdebt = moveOut = 0;
|
||||
} else if (this.outdebt && moveOut <= this.outdebt) {
|
||||
this.outdebt -= moveOut;
|
||||
moveOut = 0;
|
||||
@@ -564,6 +576,98 @@
|
||||
return this;
|
||||
}
|
||||
|
||||
csxToken() {
|
||||
var afterTag, colon, csxTag, end, firstChar, id, input, match, origin, prev, ref, token, tokens;
|
||||
firstChar = this.chunk[0];
|
||||
if (firstChar === '<') {
|
||||
match = CSX_IDENTIFIER.exec(this.chunk.slice(1));
|
||||
if (!(match && (this.csxDepth > 0 || !(prev = this.prev()) || prev.spaced || (ref = prev[0], indexOf.call(COMPARABLE_LEFT_SIDE, ref) < 0)))) {
|
||||
return 0;
|
||||
}
|
||||
[input, id, colon] = match;
|
||||
origin = this.token('CSX_TAG', id, 1, id.length);
|
||||
this.token('CALL_START', '(');
|
||||
this.token('{', '{');
|
||||
this.ends.push({
|
||||
tag: '/>',
|
||||
origin: origin,
|
||||
name: id
|
||||
});
|
||||
this.csxDepth++;
|
||||
return id.length + 1;
|
||||
} else if (csxTag = this.atCSXTag()) {
|
||||
if (this.chunk.slice(0, 2) === '/>') {
|
||||
this.pair('/>');
|
||||
this.token('}', '}', 0, 2);
|
||||
this.token('CALL_END', ')', 0, 2);
|
||||
this.csxDepth--;
|
||||
return 2;
|
||||
} else if (firstChar === '{') {
|
||||
token = this.token('(', '(');
|
||||
this.ends.push({
|
||||
tag: '}',
|
||||
origin: token
|
||||
});
|
||||
return 1;
|
||||
} else if (firstChar === '>') {
|
||||
this.pair('/>');
|
||||
origin = this.token('}', '}');
|
||||
this.token(',', ',');
|
||||
({
|
||||
tokens,
|
||||
index: end
|
||||
} = this.matchWithInterpolations(INSIDE_CSX, '>', '</', CSX_INTERPOLATION));
|
||||
this.mergeInterpolationTokens(tokens, {
|
||||
delimiter: '"'
|
||||
}, (value, i) => {
|
||||
return this.formatString(value, {
|
||||
delimiter: '>'
|
||||
});
|
||||
});
|
||||
match = CSX_IDENTIFIER.exec(this.chunk.slice(end));
|
||||
if (!match || match[0] !== csxTag.name) {
|
||||
this.error(`expected corresponding CSX closing tag for ${csxTag.name}`, csxTag.origin[2]);
|
||||
}
|
||||
afterTag = end + csxTag.name.length;
|
||||
if (this.chunk[afterTag] !== '>') {
|
||||
this.error("missing closing > after tag name", {
|
||||
offset: afterTag,
|
||||
length: 1
|
||||
});
|
||||
}
|
||||
this.token('CALL_END', ')', end, csxTag.name.length + 1);
|
||||
this.csxDepth--;
|
||||
return afterTag + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else if (this.atCSXTag(1)) {
|
||||
if (firstChar === '}') {
|
||||
this.pair(firstChar);
|
||||
this.token(')', ')');
|
||||
this.token(',', ',');
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
atCSXTag(depth = 0) {
|
||||
var i, last, ref;
|
||||
if (this.csxDepth === 0) {
|
||||
return false;
|
||||
}
|
||||
i = this.ends.length - 1;
|
||||
while (((ref = this.ends[i]) != null ? ref.tag : void 0) === 'OUTDENT' || depth-- > 0) {
|
||||
i--;
|
||||
}
|
||||
last = this.ends[i];
|
||||
return (last != null ? last.tag : void 0) === '/>' && last;
|
||||
}
|
||||
|
||||
literalToken() {
|
||||
var match, message, origin, prev, ref, ref1, ref2, ref3, skipToken, tag, token, value;
|
||||
if (match = OPERATOR.exec(this.chunk)) {
|
||||
@@ -652,19 +756,20 @@
|
||||
case ']':
|
||||
this.pair(value);
|
||||
}
|
||||
this.tokens.push(token);
|
||||
this.tokens.push(this.makeToken(tag, value));
|
||||
return value.length;
|
||||
}
|
||||
|
||||
tagParameters() {
|
||||
var i, stack, tok, tokens;
|
||||
var i, paramEndToken, stack, tok, tokens;
|
||||
if (this.tag() !== ')') {
|
||||
return this;
|
||||
}
|
||||
stack = [];
|
||||
({tokens} = this);
|
||||
i = tokens.length;
|
||||
tokens[--i][0] = 'PARAM_END';
|
||||
paramEndToken = tokens[--i];
|
||||
paramEndToken[0] = 'PARAM_END';
|
||||
while (tok = tokens[--i]) {
|
||||
switch (tok[0]) {
|
||||
case ')':
|
||||
@@ -678,6 +783,7 @@
|
||||
tok[0] = 'PARAM_START';
|
||||
return this;
|
||||
} else {
|
||||
paramEndToken[0] = 'CALL_END';
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -689,8 +795,14 @@
|
||||
return this.outdentToken(this.indent);
|
||||
}
|
||||
|
||||
matchWithInterpolations(regex, delimiter) {
|
||||
var close, column, firstToken, index, lastToken, line, nested, offsetInChunk, open, ref, str, strPart, tokens;
|
||||
matchWithInterpolations(regex, delimiter, closingDelimiter, interpolators) {
|
||||
var braceInterpolator, close, column, firstToken, index, interpolationOffset, interpolator, lastToken, line, match, nested, offsetInChunk, open, ref, rest, str, strPart, tokens;
|
||||
if (closingDelimiter == null) {
|
||||
closingDelimiter = delimiter;
|
||||
}
|
||||
if (interpolators == null) {
|
||||
interpolators = /^#\{/;
|
||||
}
|
||||
tokens = [];
|
||||
offsetInChunk = delimiter.length;
|
||||
if (this.chunk.slice(0, offsetInChunk) !== delimiter) {
|
||||
@@ -706,32 +818,43 @@
|
||||
tokens.push(this.makeToken('NEOSTRING', strPart, offsetInChunk));
|
||||
str = str.slice(strPart.length);
|
||||
offsetInChunk += strPart.length;
|
||||
if (str.slice(0, 2) !== '#{') {
|
||||
if (!(match = interpolators.exec(str))) {
|
||||
break;
|
||||
}
|
||||
[line, column] = this.getLineAndColumnFromChunk(offsetInChunk + 1);
|
||||
[interpolator] = match;
|
||||
interpolationOffset = interpolator.length - 1;
|
||||
[line, column] = this.getLineAndColumnFromChunk(offsetInChunk + interpolationOffset);
|
||||
rest = str.slice(interpolationOffset);
|
||||
({
|
||||
tokens: nested,
|
||||
index
|
||||
} = new Lexer().tokenize(str.slice(1), {
|
||||
} = new Lexer().tokenize(rest, {
|
||||
line: line,
|
||||
column: column,
|
||||
untilBalanced: true
|
||||
}));
|
||||
index += 1;
|
||||
open = nested[0], close = nested[nested.length - 1];
|
||||
open[0] = open[1] = '(';
|
||||
close[0] = close[1] = ')';
|
||||
close.origin = ['', 'end of interpolation', close[2]];
|
||||
index += interpolationOffset;
|
||||
braceInterpolator = str[index - 1] === '}';
|
||||
if (braceInterpolator) {
|
||||
open = nested[0], close = nested[nested.length - 1];
|
||||
open[0] = open[1] = '(';
|
||||
close[0] = close[1] = ')';
|
||||
close.origin = ['', 'end of interpolation', close[2]];
|
||||
}
|
||||
if (((ref = nested[1]) != null ? ref[0] : void 0) === 'TERMINATOR') {
|
||||
nested.splice(1, 1);
|
||||
}
|
||||
if (!braceInterpolator) {
|
||||
open = this.makeToken('(', '(', offsetInChunk, 0);
|
||||
close = this.makeToken(')', ')', offsetInChunk + index, 0);
|
||||
nested = [open, ...nested, close];
|
||||
}
|
||||
tokens.push(['TOKENS', nested]);
|
||||
str = str.slice(index);
|
||||
offsetInChunk += index;
|
||||
}
|
||||
if (str.slice(0, delimiter.length) !== delimiter) {
|
||||
this.error(`missing ${delimiter}`, {
|
||||
if (str.slice(0, closingDelimiter.length) !== closingDelimiter) {
|
||||
this.error(`missing ${closingDelimiter}`, {
|
||||
length: delimiter.length
|
||||
});
|
||||
}
|
||||
@@ -739,16 +862,16 @@
|
||||
firstToken[2].first_column -= delimiter.length;
|
||||
if (lastToken[1].substr(-1) === '\n') {
|
||||
lastToken[2].last_line += 1;
|
||||
lastToken[2].last_column = delimiter.length - 1;
|
||||
lastToken[2].last_column = closingDelimiter.length - 1;
|
||||
} else {
|
||||
lastToken[2].last_column += delimiter.length;
|
||||
lastToken[2].last_column += closingDelimiter.length;
|
||||
}
|
||||
if (lastToken[1].length === 0) {
|
||||
lastToken[2].last_column -= 1;
|
||||
}
|
||||
return {
|
||||
tokens,
|
||||
index: offsetInChunk + delimiter.length
|
||||
index: offsetInChunk + closingDelimiter.length
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1078,6 +1201,10 @@
|
||||
|
||||
IDENTIFIER = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+)([^\n\S]*:(?!:))?/;
|
||||
|
||||
CSX_IDENTIFIER = /^(?![\d<])((?:(?!\s)[\.\-$\w\x7f-\uffff])+)/;
|
||||
|
||||
CSX_ATTRIBUTE = /^(?!\d)((?:(?!\s)[\-$\w\x7f-\uffff])+)([^\S]*=(?!=))?/;
|
||||
|
||||
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
|
||||
|
||||
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?(\.|::)|\.{2,3})/;
|
||||
@@ -1104,6 +1231,10 @@
|
||||
|
||||
HEREDOC_DOUBLE = /^(?:[^\\"#]|\\[\s\S]|"(?!"")|\#(?!\{))*/;
|
||||
|
||||
INSIDE_CSX = /^(?:[^\{<])*/;
|
||||
|
||||
CSX_INTERPOLATION = /^(?:\{|<(?!\/))/;
|
||||
|
||||
STRING_OMIT = /((?:\\\\)+)|\\[^\S\n]*\n\s*/g;
|
||||
|
||||
SIMPLE_STRING_OMIT = /\s*\n\s*/g;
|
||||
@@ -1160,6 +1291,8 @@
|
||||
|
||||
INDEXABLE = CALLABLE.concat(['NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END', 'BOOL', 'NULL', 'UNDEFINED', '}', '::']);
|
||||
|
||||
COMPARABLE_LEFT_SIDE = ['IDENTIFIER', ')', ']', 'NUMBER'];
|
||||
|
||||
NOT_REGEX = INDEXABLE.concat(['++', '--']);
|
||||
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, Call, Class, Code, CodeFragment, Comment, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, multident, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, utility,
|
||||
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXTag, Call, Class, Code, CodeFragment, Comment, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, multident, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, utility,
|
||||
splice = [].splice,
|
||||
indexOf = [].indexOf,
|
||||
slice = [].slice;
|
||||
@@ -294,7 +294,11 @@
|
||||
}
|
||||
|
||||
wrapInParentheses(fragments) {
|
||||
return [].concat(this.makeCode('('), fragments, this.makeCode(')'));
|
||||
return [this.makeCode('('), ...fragments, this.makeCode(')')];
|
||||
}
|
||||
|
||||
wrapInBraces(fragments) {
|
||||
return [this.makeCode('{'), ...fragments, this.makeCode('}')];
|
||||
}
|
||||
|
||||
joinFragmentArrays(fragmentsList, joinStr) {
|
||||
@@ -666,7 +670,23 @@
|
||||
|
||||
};
|
||||
|
||||
exports.StringLiteral = StringLiteral = class StringLiteral extends Literal {};
|
||||
exports.StringLiteral = StringLiteral = class StringLiteral extends Literal {
|
||||
compileNode(o) {
|
||||
var res;
|
||||
return res = this.csx ? [this.makeCode(this.unquote(true))] : super.compileNode();
|
||||
}
|
||||
|
||||
unquote(literal) {
|
||||
var unquoted;
|
||||
unquoted = this.value.slice(1, -1);
|
||||
if (literal) {
|
||||
return unquoted.replace(/\\n/g, '\n').replace(/\\"/g, '"');
|
||||
} else {
|
||||
return unquoted;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.RegexLiteral = RegexLiteral = class RegexLiteral extends Literal {};
|
||||
|
||||
@@ -686,6 +706,8 @@
|
||||
|
||||
})();
|
||||
|
||||
exports.CSXTag = CSXTag = class CSXTag extends IdentifierLiteral {};
|
||||
|
||||
exports.PropertyName = PropertyName = (function() {
|
||||
class PropertyName extends Literal {};
|
||||
|
||||
@@ -1060,6 +1082,7 @@
|
||||
if (this.variable instanceof Value && this.variable.isNotCallable()) {
|
||||
this.variable.error("literal is not a function");
|
||||
}
|
||||
this.csx = this.variable.base instanceof CSXTag;
|
||||
}
|
||||
|
||||
updateLocationDataIfMissing(locationData) {
|
||||
@@ -1145,6 +1168,9 @@
|
||||
|
||||
compileNode(o) {
|
||||
var arg, argIndex, compiledArgs, fragments, j, len1, ref1, ref2;
|
||||
if (this.csx) {
|
||||
return this.compileCSX(o);
|
||||
}
|
||||
if ((ref1 = this.variable) != null) {
|
||||
ref1.front = this.front;
|
||||
}
|
||||
@@ -1169,6 +1195,26 @@
|
||||
return fragments;
|
||||
}
|
||||
|
||||
compileCSX(o) {
|
||||
var attributes, content, fragments, tag;
|
||||
[attributes, content] = this.args;
|
||||
attributes.base.csx = true;
|
||||
if (content != null) {
|
||||
content.base.csx = true;
|
||||
}
|
||||
fragments = [this.makeCode('<')];
|
||||
fragments.push(...(tag = this.variable.compileToFragments(o, LEVEL_ACCESS)));
|
||||
fragments.push(...attributes.compileToFragments(o, LEVEL_PAREN));
|
||||
if (content) {
|
||||
fragments.push(this.makeCode('>'));
|
||||
fragments.push(...content.compileNode(o, LEVEL_LIST));
|
||||
fragments.push(...[this.makeCode('</'), ...tag, this.makeCode('>')]);
|
||||
} else {
|
||||
fragments.push(this.makeCode(' />'));
|
||||
}
|
||||
return fragments;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Call.prototype.children = ['variable', 'args'];
|
||||
@@ -1497,8 +1543,20 @@
|
||||
return !this.isAssignable();
|
||||
}
|
||||
|
||||
hasSplat() {
|
||||
var j, len1, prop, ref1, splat;
|
||||
ref1 = this.properties;
|
||||
for (j = 0, len1 = ref1.length; j < len1; j++) {
|
||||
prop = ref1[j];
|
||||
if (prop instanceof Splat) {
|
||||
splat = true;
|
||||
}
|
||||
}
|
||||
return splat != null ? splat : false;
|
||||
}
|
||||
|
||||
compileNode(o) {
|
||||
var answer, i, idt, indent, isCompact, j, join, k, key, l, lastNoncom, len1, len2, len3, node, prop, props, ref1, value;
|
||||
var answer, i, idt, indent, isCompact, j, join, k, key, l, lastNoncom, len1, len2, len3, len4, node, prop, props, q, ref1, unwrappedVal, value;
|
||||
props = this.properties;
|
||||
if (this.generated) {
|
||||
for (j = 0, len1 = props.length; j < len1; j++) {
|
||||
@@ -1508,21 +1566,39 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.hasSplat()) {
|
||||
return this.compileSpread(o);
|
||||
}
|
||||
idt = o.indent += TAB;
|
||||
lastNoncom = this.lastNonComment(this.properties);
|
||||
if (this.lhs) {
|
||||
for (k = 0, len2 = props.length; k < len2; k++) {
|
||||
prop = props[k];
|
||||
if (!(prop instanceof Assign)) {
|
||||
continue;
|
||||
}
|
||||
({value} = prop);
|
||||
unwrappedVal = value.unwrapAll();
|
||||
if (unwrappedVal instanceof Arr || unwrappedVal instanceof Obj) {
|
||||
unwrappedVal.lhs = true;
|
||||
} else if (unwrappedVal instanceof Assign) {
|
||||
unwrappedVal.nestedLhs = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
isCompact = true;
|
||||
ref1 = this.properties;
|
||||
for (k = 0, len2 = ref1.length; k < len2; k++) {
|
||||
prop = ref1[k];
|
||||
if (prop instanceof Comment || (prop instanceof Assign && prop.context === 'object')) {
|
||||
for (l = 0, len3 = ref1.length; l < len3; l++) {
|
||||
prop = ref1[l];
|
||||
if (prop instanceof Comment || (prop instanceof Assign && prop.context === 'object' && !this.csx)) {
|
||||
isCompact = false;
|
||||
}
|
||||
}
|
||||
answer = [];
|
||||
answer.push(this.makeCode(`{${(isCompact ? '' : '\n')}`));
|
||||
for (i = l = 0, len3 = props.length; l < len3; i = ++l) {
|
||||
answer.push(this.makeCode(isCompact ? '' : '\n'));
|
||||
for (i = q = 0, len4 = props.length; q < len4; i = ++q) {
|
||||
prop = props[i];
|
||||
join = i === props.length - 1 ? '' : isCompact ? ', ' : prop === lastNoncom || prop instanceof Comment ? '\n' : ',\n';
|
||||
join = i === props.length - 1 ? '' : isCompact && this.csx ? ' ' : isCompact ? ', ' : prop === lastNoncom || prop instanceof Comment || this.csx ? '\n' : ',\n';
|
||||
indent = isCompact || prop instanceof Comment ? '' : idt;
|
||||
key = prop instanceof Assign && prop.context === 'object' ? prop.variable : prop instanceof Assign ? (!this.lhs ? prop.operatorToken.error(`unexpected ${prop.operatorToken.value}`) : void 0, prop.variable) : !(prop instanceof Comment) ? prop : void 0;
|
||||
if (key instanceof Value && key.hasProperties()) {
|
||||
@@ -1546,12 +1622,21 @@
|
||||
if (indent) {
|
||||
answer.push(this.makeCode(indent));
|
||||
}
|
||||
if (this.csx) {
|
||||
prop.csx = true;
|
||||
}
|
||||
if (this.csx && i === 0) {
|
||||
answer.push(this.makeCode(' '));
|
||||
}
|
||||
answer.push(...prop.compileToFragments(o, LEVEL_TOP));
|
||||
if (join) {
|
||||
answer.push(this.makeCode(join));
|
||||
}
|
||||
}
|
||||
answer.push(this.makeCode(`${(isCompact ? '' : `\n${this.tab}`)}}`));
|
||||
answer.push(this.makeCode(isCompact ? '' : `\n${this.tab}`));
|
||||
if (!this.csx) {
|
||||
answer = this.wrapInBraces(answer);
|
||||
}
|
||||
if (this.front) {
|
||||
return this.wrapInParentheses(answer);
|
||||
} else {
|
||||
@@ -1590,6 +1675,38 @@
|
||||
return results;
|
||||
}
|
||||
|
||||
compileSpread(o) {
|
||||
var addSlice, j, len1, prop, propSlices, props, slices, splatSlice;
|
||||
props = this.properties;
|
||||
splatSlice = [];
|
||||
propSlices = [];
|
||||
slices = [];
|
||||
addSlice = function() {
|
||||
if (propSlices.length) {
|
||||
slices.push(new Obj(propSlices));
|
||||
}
|
||||
if (splatSlice.length) {
|
||||
slices.push(...splatSlice);
|
||||
}
|
||||
splatSlice = [];
|
||||
return propSlices = [];
|
||||
};
|
||||
for (j = 0, len1 = props.length; j < len1; j++) {
|
||||
prop = props[j];
|
||||
if (prop instanceof Splat) {
|
||||
splatSlice.push(new Value(prop.name));
|
||||
addSlice();
|
||||
} else {
|
||||
propSlices.push(prop);
|
||||
}
|
||||
}
|
||||
addSlice();
|
||||
if (!(slices[0] instanceof Obj)) {
|
||||
slices.unshift(new Obj);
|
||||
}
|
||||
return (new Call(new Literal('Object.assign'), slices)).compileToFragments(o);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Obj.prototype.children = ['properties'];
|
||||
@@ -1714,34 +1831,41 @@
|
||||
}
|
||||
|
||||
compileNode(o) {
|
||||
var assign, executableBody, parentName, result;
|
||||
var executableBody, node, parentName;
|
||||
this.name = this.determineName();
|
||||
executableBody = this.walkBody();
|
||||
if (this.parent instanceof Value && !this.parent.hasProperties()) {
|
||||
parentName = this.parent.base.value;
|
||||
}
|
||||
this.hasNameClash = (this.name != null) && this.name === parentName;
|
||||
node = this;
|
||||
if (executableBody || this.hasNameClash) {
|
||||
this.compileNode = this.compileClassDeclaration;
|
||||
result = new ExecutableClassBody(this, executableBody).compileToFragments(o);
|
||||
this.compileNode = this.constructor.prototype.compileNode;
|
||||
} else {
|
||||
result = this.compileClassDeclaration(o);
|
||||
if ((this.name == null) && o.level === LEVEL_TOP) {
|
||||
result = this.wrapInParentheses(result);
|
||||
node = new ExecutableClassBody(node, executableBody);
|
||||
} else if ((this.name == null) && o.level === LEVEL_TOP) {
|
||||
node = new Parens(node);
|
||||
}
|
||||
if (this.boundMethods.length && this.parent) {
|
||||
if (this.variable == null) {
|
||||
this.variable = new IdentifierLiteral(o.scope.freeVariable('_class'));
|
||||
}
|
||||
if (this.variableRef == null) {
|
||||
[this.variable, this.variableRef] = this.variable.cache(o);
|
||||
}
|
||||
}
|
||||
if (this.variable) {
|
||||
assign = new Assign(this.variable, new Literal(''), null, {moduleDeclaration: this.moduleDeclaration});
|
||||
return [...assign.compileToFragments(o), ...result];
|
||||
} else {
|
||||
return result;
|
||||
node = new Assign(this.variable, node, null, {moduleDeclaration: this.moduleDeclaration});
|
||||
}
|
||||
this.compileNode = this.compileClassDeclaration;
|
||||
try {
|
||||
return node.compileToFragments(o);
|
||||
} finally {
|
||||
delete this.compileNode;
|
||||
}
|
||||
}
|
||||
|
||||
compileClassDeclaration(o) {
|
||||
var ref1, result;
|
||||
if (this.externalCtor) {
|
||||
if (this.externalCtor || this.boundMethods.length) {
|
||||
if (this.ctor == null) {
|
||||
this.ctor = this.makeDefaultConstructor();
|
||||
}
|
||||
@@ -1749,6 +1873,9 @@
|
||||
if ((ref1 = this.ctor) != null) {
|
||||
ref1.noReturn = true;
|
||||
}
|
||||
if (this.boundMethods.length) {
|
||||
this.proxyBoundMethods();
|
||||
}
|
||||
o.indent += TAB;
|
||||
result = [];
|
||||
result.push(this.makeCode("class "));
|
||||
@@ -1796,6 +1923,7 @@
|
||||
walkBody() {
|
||||
var assign, end, executableBody, expression, expressions, exprs, i, initializer, initializerExpression, j, k, len1, len2, method, properties, pushSlice, ref1, start;
|
||||
this.ctor = null;
|
||||
this.boundMethods = [];
|
||||
executableBody = null;
|
||||
initializer = [];
|
||||
({expressions} = this.body);
|
||||
@@ -1849,6 +1977,8 @@
|
||||
this.ctor = method;
|
||||
} else if (method.isStatic && method.bound) {
|
||||
method.context = this.name;
|
||||
} else if (method.bound) {
|
||||
this.boundMethods.push(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1904,8 +2034,8 @@
|
||||
if (methodName.value === 'constructor') {
|
||||
method.ctor = (this.parent ? 'derived' : 'base');
|
||||
}
|
||||
if (method.bound) {
|
||||
method.error('Methods cannot be bound functions');
|
||||
if (method.bound && method.ctor) {
|
||||
method.error('Cannot define a constructor as a bound (fat arrow) function');
|
||||
}
|
||||
}
|
||||
return method;
|
||||
@@ -1927,6 +2057,25 @@
|
||||
return ctor;
|
||||
}
|
||||
|
||||
proxyBoundMethods() {
|
||||
var method, name;
|
||||
this.ctor.thisAssignments = (function() {
|
||||
var j, len1, ref1, results;
|
||||
ref1 = this.boundMethods;
|
||||
results = [];
|
||||
for (j = 0, len1 = ref1.length; j < len1; j++) {
|
||||
method = ref1[j];
|
||||
if (this.parent) {
|
||||
method.classVariable = this.variableRef;
|
||||
}
|
||||
name = new Value(new ThisLiteral, [method.name]);
|
||||
results.push(new Assign(name, new Call(new Value(name, [new Access(new PropertyName('bind'))]), [new ThisLiteral])));
|
||||
}
|
||||
return results;
|
||||
}).call(this);
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Class.prototype.children = ['variable', 'parent', 'body'];
|
||||
@@ -2026,7 +2175,7 @@
|
||||
return this.body.traverseChildren(false, (node) => {
|
||||
if (node instanceof ThisLiteral) {
|
||||
return node.value = this.name;
|
||||
} else if (node instanceof Code && node.bound) {
|
||||
} else if (node instanceof Code && node.bound && node.isStatic) {
|
||||
return node.context = this.name;
|
||||
}
|
||||
});
|
||||
@@ -2343,6 +2492,11 @@
|
||||
if (!this.variable.isAssignable()) {
|
||||
return this.compileDestructuring(o);
|
||||
}
|
||||
if (this.variable.isObject() && this.variable.contains(function(node) {
|
||||
return node instanceof Obj && node.hasSplat();
|
||||
})) {
|
||||
return this.compileObjectDestruct(o);
|
||||
}
|
||||
}
|
||||
if (this.variable.isSplice()) {
|
||||
return this.compileSplice(o);
|
||||
@@ -2386,6 +2540,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.csx) {
|
||||
this.value.base.csxAttribute = true;
|
||||
}
|
||||
val = this.value.compileToFragments(o, LEVEL_LIST);
|
||||
compiledName = this.variable.compileToFragments(o, LEVEL_LIST);
|
||||
if (this.context === 'object') {
|
||||
@@ -2393,16 +2550,120 @@
|
||||
compiledName.unshift(this.makeCode('['));
|
||||
compiledName.push(this.makeCode(']'));
|
||||
}
|
||||
return compiledName.concat(this.makeCode(": "), val);
|
||||
return compiledName.concat(this.makeCode(this.csx ? '=' : ': '), val);
|
||||
}
|
||||
answer = compiledName.concat(this.makeCode(` ${this.context || '='} `), val);
|
||||
if (o.level > LEVEL_LIST || (isValue && this.variable.base instanceof Obj && !this.param)) {
|
||||
if (o.level > LEVEL_LIST || (o.level === LEVEL_TOP && isValue && this.variable.base instanceof Obj && !this.nestedLhs && !this.param)) {
|
||||
return this.wrapInParentheses(answer);
|
||||
} else {
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
|
||||
compileObjectDestruct(o) {
|
||||
var fragments, getPropKey, getPropName, j, len1, restElement, restElements, result, setScopeVar, traverseRest, value, valueRef;
|
||||
setScopeVar = function(prop) {
|
||||
var newVar;
|
||||
newVar = false;
|
||||
if (prop instanceof Assign && prop.value.base instanceof Obj) {
|
||||
return;
|
||||
}
|
||||
if (prop instanceof Assign) {
|
||||
if (prop.value.base instanceof IdentifierLiteral) {
|
||||
newVar = prop.value.base.compile(o);
|
||||
} else {
|
||||
newVar = prop.variable.base.compile(o);
|
||||
}
|
||||
} else {
|
||||
newVar = prop.compile(o);
|
||||
}
|
||||
if (newVar) {
|
||||
return o.scope.add(newVar, 'var', true);
|
||||
}
|
||||
};
|
||||
getPropKey = function(prop) {
|
||||
var key;
|
||||
if (prop instanceof Assign) {
|
||||
[prop.variable, key] = prop.variable.cache(o);
|
||||
return key;
|
||||
} else {
|
||||
return prop;
|
||||
}
|
||||
};
|
||||
getPropName = function(prop) {
|
||||
var cached, key;
|
||||
key = getPropKey(prop);
|
||||
cached = prop instanceof Assign && prop.variable !== key;
|
||||
if (cached || !key.isAssignable()) {
|
||||
return key;
|
||||
} else {
|
||||
return new Literal(`'${key.compile(o)}'`);
|
||||
}
|
||||
};
|
||||
traverseRest = (properties, source) => {
|
||||
var base1, index, j, len1, nestedProperties, nestedSource, nestedSourceDefault, p, prop, restElements, restIndex;
|
||||
restElements = [];
|
||||
restIndex = void 0;
|
||||
for (index = j = 0, len1 = properties.length; j < len1; index = ++j) {
|
||||
prop = properties[index];
|
||||
setScopeVar(prop.unwrap());
|
||||
if (prop instanceof Assign) {
|
||||
if (typeof (base1 = prop.value).isObject === "function" ? base1.isObject() : void 0) {
|
||||
nestedProperties = prop.value.base.properties;
|
||||
} else if (prop.value instanceof Assign && prop.value.variable.isObject()) {
|
||||
nestedProperties = prop.value.variable.base.properties;
|
||||
[prop.value.value, nestedSourceDefault] = prop.value.value.cache(o);
|
||||
}
|
||||
if (nestedProperties) {
|
||||
nestedSource = new Value(source.base, source.properties.concat([new Access(getPropKey(prop))]));
|
||||
if (nestedSourceDefault) {
|
||||
nestedSource = new Value(new Op('?', nestedSource, nestedSourceDefault));
|
||||
}
|
||||
restElements = restElements.concat(traverseRest(nestedProperties, nestedSource));
|
||||
}
|
||||
} else if (prop instanceof Splat) {
|
||||
if (restIndex != null) {
|
||||
prop.error("multiple rest elements are disallowed in object destructuring");
|
||||
}
|
||||
restIndex = index;
|
||||
restElements.push({
|
||||
name: prop.name.unwrapAll(),
|
||||
source,
|
||||
excludeProps: new Arr((function() {
|
||||
var k, len2, results;
|
||||
results = [];
|
||||
for (k = 0, len2 = properties.length; k < len2; k++) {
|
||||
p = properties[k];
|
||||
if (p !== prop) {
|
||||
results.push(getPropName(p));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
})())
|
||||
});
|
||||
}
|
||||
}
|
||||
if (restIndex != null) {
|
||||
properties.splice(restIndex, 1);
|
||||
}
|
||||
return restElements;
|
||||
};
|
||||
[this.value, valueRef] = this.value.cache(o);
|
||||
restElements = traverseRest(this.variable.base.properties, valueRef);
|
||||
result = new Block([this]);
|
||||
for (j = 0, len1 = restElements.length; j < len1; j++) {
|
||||
restElement = restElements[j];
|
||||
value = new Call(new Value(new Literal(utility('objectWithoutKeys', o))), [restElement.source, restElement.excludeProps]);
|
||||
result.push(new Assign(restElement.name, value));
|
||||
}
|
||||
fragments = result.compileToFragments(o);
|
||||
if (o.level === LEVEL_TOP) {
|
||||
fragments.shift();
|
||||
fragments.pop();
|
||||
}
|
||||
return fragments;
|
||||
}
|
||||
|
||||
compileDestructuring(o) {
|
||||
var acc, assigns, code, defaultValue, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, message, name, obj, objects, olen, ref, rest, top, val, value, vvar, vvarText;
|
||||
top = o.level === LEVEL_TOP;
|
||||
@@ -2661,7 +2922,7 @@
|
||||
}
|
||||
|
||||
compileNode(o) {
|
||||
var answer, body, condition, exprs, haveBodyParam, haveSplatParam, i, ifTrue, j, k, len1, len2, m, methodScope, modifiers, name, param, paramNames, params, paramsAfterSplat, ref, ref1, ref2, ref3, ref4, ref5, signature, splatParamName, thisAssignments, wasEmpty;
|
||||
var answer, body, boundMethodCheck, condition, exprs, haveBodyParam, haveSplatParam, i, ifTrue, j, k, len1, len2, m, methodScope, modifiers, name, param, paramNames, params, paramsAfterSplat, ref, ref1, ref2, ref3, ref4, ref5, signature, splatParamName, thisAssignments, wasEmpty;
|
||||
if (this.ctor) {
|
||||
if (this.isAsync) {
|
||||
this.name.error('Class constructor may not be async');
|
||||
@@ -2720,17 +2981,13 @@
|
||||
if (param.name instanceof Arr) {
|
||||
splatParamName = o.scope.freeVariable('arg');
|
||||
params.push(ref = new Value(new IdentifierLiteral(splatParamName)));
|
||||
exprs.push(new Assign(new Value(param.name), ref, null, {
|
||||
param: true
|
||||
}));
|
||||
exprs.push(new Assign(new Value(param.name), ref));
|
||||
} else {
|
||||
params.push(ref = param.asReference(o));
|
||||
splatParamName = fragmentsToText(ref.compileNode(o));
|
||||
}
|
||||
if (param.shouldCache()) {
|
||||
exprs.push(new Assign(new Value(param.name), ref, null, {
|
||||
param: true
|
||||
}));
|
||||
exprs.push(new Assign(new Value(param.name), ref));
|
||||
}
|
||||
} else {
|
||||
splatParamName = o.scope.freeVariable('args');
|
||||
@@ -2743,14 +3000,10 @@
|
||||
haveBodyParam = true;
|
||||
if (param.value != null) {
|
||||
condition = new Op('===', param, new UndefinedLiteral);
|
||||
ifTrue = new Assign(new Value(param.name), param.value, null, {
|
||||
param: true
|
||||
});
|
||||
ifTrue = new Assign(new Value(param.name), param.value);
|
||||
exprs.push(new If(condition, ifTrue));
|
||||
} else {
|
||||
exprs.push(new Assign(new Value(param.name), param.asReference(o), null, {
|
||||
param: true
|
||||
}));
|
||||
exprs.push(new Assign(new Value(param.name), param.asReference(o)));
|
||||
}
|
||||
}
|
||||
if (!haveSplatParam) {
|
||||
@@ -2770,6 +3023,17 @@
|
||||
param.name.eachName(function(prop) {
|
||||
return o.scope.parameter(prop.value);
|
||||
});
|
||||
if (param.name instanceof Obj && param.name.hasSplat()) {
|
||||
splatParamName = o.scope.freeVariable('arg');
|
||||
o.scope.parameter(splatParamName);
|
||||
ref = new Value(new IdentifierLiteral(splatParamName));
|
||||
exprs.push(new Assign(new Value(param.name), ref));
|
||||
if ((param.value != null) && !param.assignedInBody) {
|
||||
ref = new Assign(ref, param.value, null, {
|
||||
param: true
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
o.scope.parameter(fragmentsToText((param.value != null ? param : ref).compileToFragments(o)));
|
||||
}
|
||||
@@ -2805,6 +3069,10 @@
|
||||
this.body.expressions.unshift(...thisAssignments);
|
||||
}
|
||||
this.body.expressions.unshift(...exprs);
|
||||
if (this.isMethod && this.bound && !this.isStatic && this.classVariable) {
|
||||
boundMethodCheck = new Value(new Literal(utility('boundMethodCheck', o)));
|
||||
this.body.expressions.unshift(new Call(boundMethodCheck, [new Value(new ThisLiteral), this.classVariable]));
|
||||
}
|
||||
if (!(wasEmpty || this.noReturn)) {
|
||||
this.body.makeReturn();
|
||||
}
|
||||
@@ -2951,12 +3219,12 @@
|
||||
|
||||
exports.Param = Param = (function() {
|
||||
class Param extends Base {
|
||||
constructor(name1, value1, splat) {
|
||||
constructor(name1, value1, splat1) {
|
||||
var message, token;
|
||||
super();
|
||||
this.name = name1;
|
||||
this.value = value1;
|
||||
this.splat = splat;
|
||||
this.splat = splat1;
|
||||
message = isUnassignable(this.name.unwrapAll().value);
|
||||
if (message) {
|
||||
this.name.error(message);
|
||||
@@ -3672,12 +3940,15 @@
|
||||
compileNode(o) {
|
||||
var bare, expr, fragments;
|
||||
expr = this.body.unwrap();
|
||||
if (expr instanceof Value && expr.isAtomic()) {
|
||||
if (expr instanceof Value && expr.isAtomic() && !this.csxAttribute) {
|
||||
expr.front = this.front;
|
||||
return expr.compileToFragments(o);
|
||||
}
|
||||
fragments = expr.compileToFragments(o, LEVEL_PAREN);
|
||||
bare = o.level < LEVEL_OP && (expr instanceof Op || expr instanceof Call || (expr instanceof For && expr.returns)) && (o.level < LEVEL_COND || fragments.length <= 3);
|
||||
if (this.csxAttribute) {
|
||||
return this.wrapInBraces(fragments);
|
||||
}
|
||||
if (bare) {
|
||||
return fragments;
|
||||
} else {
|
||||
@@ -3709,7 +3980,12 @@
|
||||
}
|
||||
|
||||
compileNode(o) {
|
||||
var element, elements, expr, fragments, j, len1, value;
|
||||
var code, element, elements, expr, fragments, j, len1, value, wrapped;
|
||||
if (this.csxAttribute) {
|
||||
wrapped = new Parens(new StringWithInterpolations(this.body));
|
||||
wrapped.csxAttribute = true;
|
||||
return wrapped.compileNode(o);
|
||||
}
|
||||
expr = this.body.unwrap();
|
||||
elements = [];
|
||||
expr.traverseChildren(false, function(node) {
|
||||
@@ -3723,29 +3999,47 @@
|
||||
return true;
|
||||
});
|
||||
fragments = [];
|
||||
fragments.push(this.makeCode('`'));
|
||||
if (!this.csx) {
|
||||
fragments.push(this.makeCode('`'));
|
||||
}
|
||||
for (j = 0, len1 = elements.length; j < len1; j++) {
|
||||
element = elements[j];
|
||||
if (element instanceof StringLiteral) {
|
||||
value = element.value.slice(1, -1);
|
||||
value = value.replace(/(\\*)(`|\$\{)/g, function(match, backslashes, toBeEscaped) {
|
||||
if (backslashes.length % 2 === 0) {
|
||||
return `${backslashes}\\${toBeEscaped}`;
|
||||
} else {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
value = element.unquote(this.csx);
|
||||
if (!this.csx) {
|
||||
value = value.replace(/(\\*)(`|\$\{)/g, function(match, backslashes, toBeEscaped) {
|
||||
if (backslashes.length % 2 === 0) {
|
||||
return `${backslashes}\\${toBeEscaped}`;
|
||||
} else {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
}
|
||||
fragments.push(this.makeCode(value));
|
||||
} else {
|
||||
fragments.push(this.makeCode('${'));
|
||||
fragments.push(...element.compileToFragments(o, LEVEL_PAREN));
|
||||
fragments.push(this.makeCode('}'));
|
||||
if (!this.csx) {
|
||||
fragments.push(this.makeCode('$'));
|
||||
}
|
||||
code = element.compileToFragments(o, LEVEL_PAREN);
|
||||
if (!this.isNestedTag(element)) {
|
||||
code = this.wrapInBraces(code);
|
||||
}
|
||||
fragments.push(...code);
|
||||
}
|
||||
}
|
||||
fragments.push(this.makeCode('`'));
|
||||
if (!this.csx) {
|
||||
fragments.push(this.makeCode('`'));
|
||||
}
|
||||
return fragments;
|
||||
}
|
||||
|
||||
isNestedTag(element) {
|
||||
var call, exprs, ref1;
|
||||
exprs = element != null ? (ref1 = element.body) != null ? ref1.expressions : void 0 : void 0;
|
||||
call = exprs != null ? exprs[0] : void 0;
|
||||
return this.csx && exprs && exprs.length === 1 && call instanceof Call && call.csx;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
StringWithInterpolations.prototype.children = ['body'];
|
||||
@@ -4161,6 +4455,12 @@
|
||||
modulo: function() {
|
||||
return 'function(a, b) { return (+a % (b = +b) + b) % b; }';
|
||||
},
|
||||
objectWithoutKeys: function() {
|
||||
return "function(o, ks) { var res = {}; for (var k in o) ([].indexOf.call(ks, k) < 0 && {}.hasOwnProperty.call(o, k)) && (res[k] = o[k]); return res; }";
|
||||
},
|
||||
boundMethodCheck: function() {
|
||||
return "function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error('Bound instance method accessed before binding'); } }";
|
||||
},
|
||||
hasProp: function() {
|
||||
return '{}.hasOwnProperty';
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat;
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var CoffeeScript, Module, binary, child_process, ext, findExtension, fork, helpers, i, len, loadFile, path, ref;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var CoffeeScript, addHistory, addMultilineHandler, fs, getCommandId, merge, nodeREPL, path, replDefaults, runInContext, updateSyntaxError, vm;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
return results;
|
||||
})();
|
||||
ast = CoffeeScript.nodes(tokens);
|
||||
ast = new Block([new Assign(new Value(new Literal('_')), ast, '=')]);
|
||||
ast = new Block([new Assign(new Value(new Literal('__')), ast, '=')]);
|
||||
js = ast.compile({
|
||||
bare: true,
|
||||
locals: Object.keys(context),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, generate, k, left, len, rite,
|
||||
var BALANCED_PAIRS, CALL_CLOSERS, CONTROL_IN_IMPLICIT, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, generate, k, left, len, rite, throwSyntaxError,
|
||||
indexOf = [].indexOf;
|
||||
|
||||
({throwSyntaxError} = require('./helpers'));
|
||||
|
||||
generate = function(tag, value, origin) {
|
||||
var tok;
|
||||
tok = [tag, value];
|
||||
@@ -24,6 +26,7 @@
|
||||
this.tagPostfixConditionals();
|
||||
this.addImplicitBracesAndParens();
|
||||
this.addLocationDataToGeneratedTokens();
|
||||
this.enforceValidCSXAttributes();
|
||||
this.fixOutdentLocationData();
|
||||
return this.tokens;
|
||||
}
|
||||
@@ -38,7 +41,7 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
detectEnd(i, condition, action) {
|
||||
detectEnd(i, condition, action, opts = {}) {
|
||||
var levels, ref, ref1, token, tokens;
|
||||
({tokens} = this);
|
||||
levels = 0;
|
||||
@@ -46,14 +49,17 @@
|
||||
if (levels === 0 && condition.call(this, token, i)) {
|
||||
return action.call(this, token, i);
|
||||
}
|
||||
if (!token || levels < 0) {
|
||||
return action.call(this, token, i - 1);
|
||||
}
|
||||
if (ref = token[0], indexOf.call(EXPRESSION_START, ref) >= 0) {
|
||||
levels += 1;
|
||||
} else if (ref1 = token[0], indexOf.call(EXPRESSION_END, ref1) >= 0) {
|
||||
levels -= 1;
|
||||
}
|
||||
if (levels < 0) {
|
||||
if (opts.returnOnNegativeLevel) {
|
||||
return;
|
||||
}
|
||||
return action.call(this, token, i);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return i - 1;
|
||||
@@ -77,10 +83,10 @@
|
||||
var action, condition;
|
||||
condition = function(token, i) {
|
||||
var ref;
|
||||
return ((ref = token[0]) === ')' || ref === 'CALL_END') || token[0] === 'OUTDENT' && this.tag(i - 1) === ')';
|
||||
return (ref = token[0]) === ')' || ref === 'CALL_END';
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens[token[0] === 'OUTDENT' ? i - 1 : i][0] = 'CALL_END';
|
||||
return token[0] = 'CALL_END';
|
||||
};
|
||||
return this.scanTokens(function(token, i) {
|
||||
if (token[0] === 'CALL_START') {
|
||||
@@ -168,10 +174,10 @@
|
||||
stack = [];
|
||||
start = null;
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, isImplicit, isImplicitCall, isImplicitObject, k, newLine, nextTag, offset, prevTag, prevToken, ref, ref1, ref2, s, sameLine, stackIdx, stackItem, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag;
|
||||
var endImplicitCall, endImplicitObject, forward, implicitObjectContinues, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, isImplicit, isImplicitCall, isImplicitObject, k, newLine, nextTag, nextToken, offset, prevTag, prevToken, ref, s, sameLine, stackIdx, stackItem, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag;
|
||||
[tag] = token;
|
||||
[prevTag] = prevToken = i > 0 ? tokens[i - 1] : [];
|
||||
[nextTag] = i < tokens.length - 1 ? tokens[i + 1] : [];
|
||||
[nextTag] = nextToken = i < tokens.length - 1 ? tokens[i + 1] : [];
|
||||
stackTop = function() {
|
||||
return stack[stack.length - 1];
|
||||
};
|
||||
@@ -202,27 +208,21 @@
|
||||
var ref;
|
||||
return inImplicit() && ((ref = stackTop()) != null ? ref[0] : void 0) === 'CONTROL';
|
||||
};
|
||||
startImplicitCall = function(j) {
|
||||
var idx;
|
||||
idx = j != null ? j : i;
|
||||
startImplicitCall = function(idx) {
|
||||
stack.push([
|
||||
'(', idx, {
|
||||
ours: true
|
||||
}
|
||||
]);
|
||||
tokens.splice(idx, 0, generate('CALL_START', '('));
|
||||
if (j == null) {
|
||||
return i += 1;
|
||||
}
|
||||
return tokens.splice(idx, 0, generate('CALL_START', '('));
|
||||
};
|
||||
endImplicitCall = function() {
|
||||
stack.pop();
|
||||
tokens.splice(i, 0, generate('CALL_END', ')', ['', 'end of input', token[2]]));
|
||||
return i += 1;
|
||||
};
|
||||
startImplicitObject = function(j, startsLine = true) {
|
||||
var idx, val;
|
||||
idx = j != null ? j : i;
|
||||
startImplicitObject = function(idx, startsLine = true) {
|
||||
var val;
|
||||
stack.push([
|
||||
'{', idx, {
|
||||
sameLine: true,
|
||||
@@ -232,10 +232,7 @@
|
||||
]);
|
||||
val = new String('{');
|
||||
val.generated = true;
|
||||
tokens.splice(idx, 0, generate('{', val, token));
|
||||
if (j == null) {
|
||||
return i += 1;
|
||||
}
|
||||
return tokens.splice(idx, 0, generate('{', val, token));
|
||||
};
|
||||
endImplicitObject = function(j) {
|
||||
j = j != null ? j : i;
|
||||
@@ -243,7 +240,22 @@
|
||||
tokens.splice(j, 0, generate('}', '}', token));
|
||||
return i += 1;
|
||||
};
|
||||
if (inImplicitCall() && (tag === 'IF' || tag === 'TRY' || tag === 'FINALLY' || tag === 'CATCH' || tag === 'CLASS' || tag === 'SWITCH')) {
|
||||
implicitObjectContinues = (j) => {
|
||||
var nextTerminatorIdx;
|
||||
nextTerminatorIdx = null;
|
||||
this.detectEnd(j, function(token) {
|
||||
return token[0] === 'TERMINATOR';
|
||||
}, function(token, i) {
|
||||
return nextTerminatorIdx = i;
|
||||
}, {
|
||||
returnOnNegativeLevel: true
|
||||
});
|
||||
if (nextTerminatorIdx == null) {
|
||||
return false;
|
||||
}
|
||||
return this.looksObjectish(nextTerminatorIdx + 1);
|
||||
};
|
||||
if ((inImplicitCall() || inImplicitObject()) && indexOf.call(CONTROL_IN_IMPLICIT, tag) >= 0 || inImplicitObject() && prevTag === ':' && tag === 'FOR') {
|
||||
stack.push([
|
||||
'CONTROL', i, {
|
||||
ours: true
|
||||
@@ -252,9 +264,13 @@
|
||||
return forward(1);
|
||||
}
|
||||
if (tag === 'INDENT' && inImplicit()) {
|
||||
if (prevTag !== '=>' && prevTag !== '->' && prevTag !== '[' && prevTag !== '(' && prevTag !== ',' && prevTag !== '{' && prevTag !== 'TRY' && prevTag !== 'ELSE' && prevTag !== '=') {
|
||||
while (inImplicitCall()) {
|
||||
endImplicitCall();
|
||||
if (prevTag !== '=>' && prevTag !== '->' && prevTag !== '[' && prevTag !== '(' && prevTag !== ',' && prevTag !== '{' && prevTag !== 'ELSE' && prevTag !== '=') {
|
||||
while (inImplicitCall() || inImplicitObject() && prevTag !== ':') {
|
||||
if (inImplicitCall()) {
|
||||
endImplicitCall();
|
||||
} else {
|
||||
endImplicitObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inImplicitControl()) {
|
||||
@@ -279,7 +295,7 @@
|
||||
}
|
||||
start = stack.pop();
|
||||
}
|
||||
if ((indexOf.call(IMPLICIT_FUNC, tag) >= 0 && token.spaced || tag === '?' && i > 0 && !tokens[i - 1].spaced) && (indexOf.call(IMPLICIT_CALL, nextTag) >= 0 || indexOf.call(IMPLICIT_UNSPACED_CALL, nextTag) >= 0 && !((ref = tokens[i + 1]) != null ? ref.spaced : void 0) && !((ref1 = tokens[i + 1]) != null ? ref1.newLine : void 0))) {
|
||||
if ((indexOf.call(IMPLICIT_FUNC, tag) >= 0 && token.spaced || tag === '?' && i > 0 && !tokens[i - 1].spaced) && (indexOf.call(IMPLICIT_CALL, nextTag) >= 0 || indexOf.call(IMPLICIT_UNSPACED_CALL, nextTag) >= 0 && !nextToken.spaced && !nextToken.newLine)) {
|
||||
if (tag === '?') {
|
||||
tag = token[0] = 'FUNC_EXIST';
|
||||
}
|
||||
@@ -293,9 +309,9 @@
|
||||
}
|
||||
if (tag === ':') {
|
||||
s = (function() {
|
||||
var ref2;
|
||||
var ref;
|
||||
switch (false) {
|
||||
case ref2 = this.tag(i - 1), indexOf.call(EXPRESSION_END, ref2) < 0:
|
||||
case ref = this.tag(i - 1), indexOf.call(EXPRESSION_END, ref) < 0:
|
||||
return start[1];
|
||||
case this.tag(i - 2) !== '@':
|
||||
return i - 2;
|
||||
@@ -306,8 +322,7 @@
|
||||
while (this.tag(s - 2) === 'HERECOMMENT') {
|
||||
s -= 2;
|
||||
}
|
||||
this.insideForDeclaration = nextTag === 'FOR';
|
||||
startsLine = s === 0 || (ref2 = this.tag(s - 1), indexOf.call(LINEBREAKS, ref2) >= 0) || tokens[s - 1].newLine;
|
||||
startsLine = s === 0 || (ref = this.tag(s - 1), indexOf.call(LINEBREAKS, ref) >= 0) || tokens[s - 1].newLine;
|
||||
if (stackTop()) {
|
||||
[stackTag, stackIdx] = stackTop();
|
||||
if ((stackTag === '{' || stackTag === 'INDENT' && this.tag(stackIdx - 1) === '{') && (startsLine || this.tag(s - 1) === ',' || this.tag(s - 1) === '{')) {
|
||||
@@ -331,7 +346,7 @@
|
||||
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop();
|
||||
if (inImplicitCall() && prevTag !== ',') {
|
||||
endImplicitCall();
|
||||
} else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':') {
|
||||
} else if (inImplicitObject() && sameLine && tag !== 'TERMINATOR' && prevTag !== ':' && !((tag === 'POST_IF' || tag === 'FOR' || tag === 'WHILE' || tag === 'UNTIL') && startsLine && implicitObjectContinues(i + 1))) {
|
||||
endImplicitObject();
|
||||
} else if (inImplicitObject() && tag === 'TERMINATOR' && prevTag !== ',' && !(startsLine && this.looksObjectish(i + 1))) {
|
||||
if (nextTag === 'HERECOMMENT') {
|
||||
@@ -343,7 +358,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && !this.insideForDeclaration && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
|
||||
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
|
||||
offset = nextTag === 'OUTDENT' ? 1 : 0;
|
||||
while (inImplicitObject()) {
|
||||
endImplicitObject(i + offset);
|
||||
@@ -353,6 +368,19 @@
|
||||
});
|
||||
}
|
||||
|
||||
enforceValidCSXAttributes() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var next, ref;
|
||||
if (token.csxColon) {
|
||||
next = tokens[i + 1];
|
||||
if ((ref = next[0]) !== 'STRING_START' && ref !== 'STRING' && ref !== '(') {
|
||||
throwSyntaxError('expected wrapped or quoted CSX attribute', next[2]);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
||||
addLocationDataToGeneratedTokens() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var column, line, nextLocation, prevLocation, ref, ref1;
|
||||
@@ -434,6 +462,11 @@
|
||||
return 2 + j;
|
||||
}
|
||||
}
|
||||
if ((tag === '->' || tag === '=>') && (this.tag(i + 1) === ',' || this.tag(i + 1) === '.' && token.newLine)) {
|
||||
[indent, outdent] = this.indentation(tokens[i]);
|
||||
tokens.splice(i + 1, 0, indent, outdent);
|
||||
return 1;
|
||||
}
|
||||
if (indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) {
|
||||
starter = tag;
|
||||
[indent, outdent] = this.indentation(tokens[i]);
|
||||
@@ -519,7 +552,7 @@
|
||||
|
||||
IMPLICIT_FUNC = ['IDENTIFIER', 'PROPERTY', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
|
||||
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START', 'JS', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'UNDEFINED', 'NULL', 'BOOL', 'UNARY', 'YIELD', 'AWAIT', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'CSX_TAG', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START', 'JS', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'UNDEFINED', 'NULL', 'BOOL', 'UNARY', 'YIELD', 'AWAIT', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
||||
|
||||
IMPLICIT_UNSPACED_CALL = ['+', '-'];
|
||||
|
||||
@@ -533,4 +566,6 @@
|
||||
|
||||
CALL_CLOSERS = ['.', '?.', '::', '?::'];
|
||||
|
||||
CONTROL_IN_IMPLICIT = ['IF', 'TRY', 'FINALLY', 'CATCH', 'CLASS', 'SWITCH'];
|
||||
|
||||
}).call(this);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var Scope,
|
||||
indexOf = [].indexOf;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by CoffeeScript 2.0.0-beta2
|
||||
// Generated by CoffeeScript 2.0.0-beta3
|
||||
(function() {
|
||||
var LineMap, SourceMap;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"compiler"
|
||||
],
|
||||
"author": "Jeremy Ashkenas",
|
||||
"version": "2.0.0-beta2",
|
||||
"version": "2.0.0-beta3",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=7.6.0"
|
||||
|
||||
@@ -61,10 +61,14 @@ notSources = {}
|
||||
watchedDirs = {}
|
||||
optionParser = null
|
||||
|
||||
exports.buildCSOptionParser = buildCSOptionParser = ->
|
||||
new optparse.OptionParser SWITCHES, BANNER
|
||||
|
||||
# Run `coffee` by parsing passed options and determining what action to take.
|
||||
# Many flags cause us to divert before compiling anything. Flags passed after
|
||||
# `--` will be passed verbatim to your script as arguments in `process.argv`
|
||||
exports.run = ->
|
||||
optionParser = buildCSOptionParser()
|
||||
parseOptions()
|
||||
# Make the REPL *CLI* use the global context so as to (a) be consistent with the
|
||||
# `node` REPL CLI and, therefore, (b) make packages that modify native prototypes
|
||||
@@ -400,7 +404,6 @@ printTokens = (tokens) ->
|
||||
# Use the [OptionParser module](optparse.html) to extract all options from
|
||||
# `process.argv` that are specified in `SWITCHES`.
|
||||
parseOptions = ->
|
||||
optionParser = new optparse.OptionParser SWITCHES, BANNER
|
||||
o = opts = optionParser.parse process.argv[2..]
|
||||
o.compile or= !!o.output
|
||||
o.run = not (o.compile or o.print or o.map)
|
||||
@@ -449,7 +452,7 @@ forkNode = ->
|
||||
# Print the `--help` usage message and exit. Deprecated switches are not
|
||||
# shown.
|
||||
usage = ->
|
||||
printLine (new optparse.OptionParser SWITCHES, BANNER).help()
|
||||
printLine optionParser.help()
|
||||
|
||||
# Print the `--version` message and exit.
|
||||
version = ->
|
||||
|
||||
@@ -142,6 +142,7 @@ grammar =
|
||||
|
||||
Identifier: [
|
||||
o 'IDENTIFIER', -> new IdentifierLiteral $1
|
||||
o 'CSX_TAG', -> new CSXTag $1
|
||||
]
|
||||
|
||||
Property: [
|
||||
@@ -189,6 +190,7 @@ grammar =
|
||||
# the ordinary **Assign** is that these allow numbers and strings as keys.
|
||||
AssignObj: [
|
||||
o 'ObjAssignable', -> new Value $1
|
||||
o 'ObjRestValue'
|
||||
o 'ObjAssignable : Expression', -> new Assign LOC(1)(new Value $1), $3, 'object',
|
||||
operatorToken: LOC(2)(new Literal $2)
|
||||
o 'ObjAssignable :
|
||||
@@ -213,6 +215,28 @@ grammar =
|
||||
o 'AlphaNumeric'
|
||||
]
|
||||
|
||||
# Object literal spread properties.
|
||||
ObjRestValue: [
|
||||
o 'SimpleObjAssignable ...', -> new Splat new Value $1
|
||||
o 'ObjSpreadExpr ...', -> new Splat $1
|
||||
]
|
||||
|
||||
ObjSpreadExpr: [
|
||||
o 'ObjSpreadIdentifier'
|
||||
o 'Object'
|
||||
o 'Parenthetical'
|
||||
o 'Super'
|
||||
o 'This'
|
||||
o 'SUPER Arguments', -> new SuperCall LOC(1)(new Super), $2
|
||||
o 'SimpleObjAssignable Arguments', -> new Call (new Value $1), $2
|
||||
o 'ObjSpreadExpr Arguments', -> new Call $1, $2
|
||||
]
|
||||
|
||||
ObjSpreadIdentifier: [
|
||||
o 'SimpleObjAssignable . Property', -> (new Value $1).add(new Access $3)
|
||||
o 'SimpleObjAssignable INDEX_START IndexValue INDEX_END', -> (new Value $1).add($3)
|
||||
]
|
||||
|
||||
# A return statement from a function body.
|
||||
Return: [
|
||||
o 'RETURN Expression', -> new Return $2
|
||||
@@ -700,7 +724,6 @@ grammar =
|
||||
Expression', -> new Assign $1, $4, $2
|
||||
]
|
||||
|
||||
|
||||
# Precedence
|
||||
# ----------
|
||||
|
||||
|
||||
177
src/lexer.coffee
177
src/lexer.coffee
@@ -48,6 +48,7 @@ exports.Lexer = class Lexer
|
||||
@seenExport = no # Used to recognize EXPORT FROM? AS? tokens.
|
||||
@importSpecifierList = no # Used to identify when in an IMPORT {...} FROM? ...
|
||||
@exportSpecifierList = no # Used to identify when in an EXPORT {...} FROM? ...
|
||||
@csxDepth = 0 # Used to optimize CSX checks, how deep in CSX we are.
|
||||
|
||||
@chunkLine =
|
||||
opts.line or 0 # The start line for the current @chunk.
|
||||
@@ -67,6 +68,7 @@ exports.Lexer = class Lexer
|
||||
@lineToken() or
|
||||
@stringToken() or
|
||||
@numberToken() or
|
||||
@csxToken() or
|
||||
@regexToken() or
|
||||
@jsToken() or
|
||||
@literalToken()
|
||||
@@ -79,7 +81,7 @@ exports.Lexer = class Lexer
|
||||
return {@tokens, index: i} if opts.untilBalanced and @ends.length is 0
|
||||
|
||||
@closeIndentation()
|
||||
@error "missing #{end.tag}", end.origin[2] if end = @ends.pop()
|
||||
@error "missing #{end.tag}", (end.origin ? end)[2] if end = @ends.pop()
|
||||
return @tokens if opts.rewrite is off
|
||||
(new Rewriter).rewrite @tokens
|
||||
|
||||
@@ -105,7 +107,9 @@ exports.Lexer = class Lexer
|
||||
# referenced as property names here, so you can still do `jQuery.is()` even
|
||||
# though `is` means `===` otherwise.
|
||||
identifierToken: ->
|
||||
return 0 unless match = IDENTIFIER.exec @chunk
|
||||
inCSXTag = @atCSXTag()
|
||||
regex = if inCSXTag then CSX_ATTRIBUTE else IDENTIFIER
|
||||
return 0 unless match = regex.exec @chunk
|
||||
[input, id, colon] = match
|
||||
|
||||
# Preserve length of id for location data
|
||||
@@ -205,8 +209,11 @@ exports.Lexer = class Lexer
|
||||
[tagToken[2].first_line, tagToken[2].first_column] =
|
||||
[poppedToken[2].first_line, poppedToken[2].first_column]
|
||||
if colon
|
||||
colonOffset = input.lastIndexOf ':'
|
||||
@token ':', ':', colonOffset, colon.length
|
||||
colonOffset = input.lastIndexOf if inCSXTag then '=' else ':'
|
||||
colonToken = @token ':', ':', colonOffset, colon.length
|
||||
colonToken.csxColon = yes if inCSXTag # used by rewriter
|
||||
if inCSXTag and tag is 'IDENTIFIER' and prev[0] isnt ':'
|
||||
@token ',', ',', 0, 0, tagToken
|
||||
|
||||
input.length
|
||||
|
||||
@@ -289,6 +296,9 @@ exports.Lexer = class Lexer
|
||||
' '
|
||||
value
|
||||
|
||||
if @atCSXTag()
|
||||
@token ',', ',', 0, 0, @prev
|
||||
|
||||
end
|
||||
|
||||
# Matches and consumes comments.
|
||||
@@ -432,7 +442,7 @@ exports.Lexer = class Lexer
|
||||
while moveOut > 0
|
||||
lastIndent = @indents[@indents.length - 1]
|
||||
if not lastIndent
|
||||
moveOut = 0
|
||||
@outdebt = moveOut = 0
|
||||
else if @outdebt and moveOut <= @outdebt
|
||||
@outdebt -= moveOut
|
||||
moveOut = 0
|
||||
@@ -475,6 +485,76 @@ exports.Lexer = class Lexer
|
||||
@tokens.pop() if @value() is '\\'
|
||||
this
|
||||
|
||||
# CSX is like JSX but for CoffeeScript.
|
||||
csxToken: ->
|
||||
firstChar = @chunk[0]
|
||||
if firstChar is '<'
|
||||
match = CSX_IDENTIFIER.exec @chunk[1...]
|
||||
return 0 unless match and (
|
||||
@csxDepth > 0 or
|
||||
# Not the right hand side of an unspaced comparison (i.e. `a<b`).
|
||||
not (prev = @prev()) or
|
||||
prev.spaced or
|
||||
prev[0] not in COMPARABLE_LEFT_SIDE
|
||||
)
|
||||
[input, id, colon] = match
|
||||
origin = @token 'CSX_TAG', id, 1, id.length
|
||||
@token 'CALL_START', '('
|
||||
@token '{', '{'
|
||||
@ends.push tag: '/>', origin: origin, name: id
|
||||
@csxDepth++
|
||||
return id.length + 1
|
||||
else if csxTag = @atCSXTag()
|
||||
if @chunk[...2] is '/>'
|
||||
@pair '/>'
|
||||
@token '}', '}', 0, 2
|
||||
@token 'CALL_END', ')', 0, 2
|
||||
@csxDepth--
|
||||
return 2
|
||||
else if firstChar is '{'
|
||||
token = @token '(', '('
|
||||
@ends.push {tag: '}', origin: token}
|
||||
return 1
|
||||
else if firstChar is '>'
|
||||
# Ignore terminators inside a tag.
|
||||
@pair '/>' # As if the current tag was self-closing.
|
||||
origin = @token '}', '}'
|
||||
@token ',', ','
|
||||
{tokens, index: end} =
|
||||
@matchWithInterpolations INSIDE_CSX, '>', '</', CSX_INTERPOLATION
|
||||
@mergeInterpolationTokens tokens, {delimiter: '"'}, (value, i) =>
|
||||
@formatString value, delimiter: '>'
|
||||
match = CSX_IDENTIFIER.exec @chunk[end...]
|
||||
if not match or match[0] isnt csxTag.name
|
||||
@error "expected corresponding CSX closing tag for #{csxTag.name}",
|
||||
csxTag.origin[2]
|
||||
afterTag = end + csxTag.name.length
|
||||
if @chunk[afterTag] isnt '>'
|
||||
@error "missing closing > after tag name", offset: afterTag, length: 1
|
||||
# +1 for the closing `>`.
|
||||
@token 'CALL_END', ')', end, csxTag.name.length + 1
|
||||
@csxDepth--
|
||||
return afterTag + 1
|
||||
else
|
||||
return 0
|
||||
else if @atCSXTag 1
|
||||
if firstChar is '}'
|
||||
@pair firstChar
|
||||
@token ')', ')'
|
||||
@token ',', ','
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
else
|
||||
return 0
|
||||
|
||||
atCSXTag: (depth = 0) ->
|
||||
return no if @csxDepth is 0
|
||||
i = @ends.length - 1
|
||||
i-- while @ends[i]?.tag is 'OUTDENT' or depth-- > 0 # Ignore indents.
|
||||
last = @ends[i]
|
||||
last?.tag is '/>' and last
|
||||
|
||||
# We treat all other single characters as a token. E.g.: `( ) , . !`
|
||||
# Multi-character operators are also literal tokens, so that Jison can assign
|
||||
# the proper order of operations. There are some symbols that we tag specially
|
||||
@@ -535,7 +615,7 @@ exports.Lexer = class Lexer
|
||||
switch value
|
||||
when '(', '{', '[' then @ends.push {tag: INVERSES[value], origin: token}
|
||||
when ')', '}', ']' then @pair value
|
||||
@tokens.push token
|
||||
@tokens.push @makeToken tag, value
|
||||
value.length
|
||||
|
||||
# Token Manipulators
|
||||
@@ -549,7 +629,8 @@ exports.Lexer = class Lexer
|
||||
stack = []
|
||||
{tokens} = this
|
||||
i = tokens.length
|
||||
tokens[--i][0] = 'PARAM_END'
|
||||
paramEndToken = tokens[--i]
|
||||
paramEndToken[0] = 'PARAM_END'
|
||||
while tok = tokens[--i]
|
||||
switch tok[0]
|
||||
when ')'
|
||||
@@ -559,7 +640,9 @@ exports.Lexer = class Lexer
|
||||
else if tok[0] is '('
|
||||
tok[0] = 'PARAM_START'
|
||||
return this
|
||||
else return this
|
||||
else
|
||||
paramEndToken[0] = 'CALL_END'
|
||||
return this
|
||||
this
|
||||
|
||||
# Close up all remaining open blocks at the end of the file.
|
||||
@@ -579,10 +662,16 @@ exports.Lexer = class Lexer
|
||||
# `#{` if interpolations are desired).
|
||||
# - `delimiter` is the delimiter of the token. Examples are `'`, `"`, `'''`,
|
||||
# `"""` and `///`.
|
||||
# - `closingDelimiter` is different from `delimiter` only in CSX
|
||||
# - `interpolators` matches the start of an interpolation, for CSX it's both
|
||||
# `{` and `<` (i.e. nested CSX tag)
|
||||
#
|
||||
# This method allows us to have strings within interpolations within strings,
|
||||
# ad infinitum.
|
||||
matchWithInterpolations: (regex, delimiter) ->
|
||||
matchWithInterpolations: (regex, delimiter, closingDelimiter, interpolators) ->
|
||||
closingDelimiter ?= delimiter
|
||||
interpolators ?= /^#\{/
|
||||
|
||||
tokens = []
|
||||
offsetInChunk = delimiter.length
|
||||
return null unless @chunk[...offsetInChunk] is delimiter
|
||||
@@ -598,44 +687,55 @@ exports.Lexer = class Lexer
|
||||
str = str[strPart.length..]
|
||||
offsetInChunk += strPart.length
|
||||
|
||||
break unless str[...2] is '#{'
|
||||
break unless match = interpolators.exec str
|
||||
[interpolator] = match
|
||||
|
||||
# The `1`s are to remove the `#` in `#{`.
|
||||
[line, column] = @getLineAndColumnFromChunk offsetInChunk + 1
|
||||
# To remove the `#` in `#{`.
|
||||
interpolationOffset = interpolator.length - 1
|
||||
[line, column] = @getLineAndColumnFromChunk offsetInChunk + interpolationOffset
|
||||
rest = str[interpolationOffset..]
|
||||
{tokens: nested, index} =
|
||||
new Lexer().tokenize str[1..], line: line, column: column, untilBalanced: on
|
||||
# Skip the trailing `}`.
|
||||
index += 1
|
||||
new Lexer().tokenize rest, line: line, column: column, untilBalanced: on
|
||||
# Account for the `#` in `#{`
|
||||
index += interpolationOffset
|
||||
|
||||
# Turn the leading and trailing `{` and `}` into parentheses. Unnecessary
|
||||
# parentheses will be removed later.
|
||||
[open, ..., close] = nested
|
||||
open[0] = open[1] = '('
|
||||
close[0] = close[1] = ')'
|
||||
close.origin = ['', 'end of interpolation', close[2]]
|
||||
braceInterpolator = str[index - 1] is '}'
|
||||
if braceInterpolator
|
||||
# Turn the leading and trailing `{` and `}` into parentheses. Unnecessary
|
||||
# parentheses will be removed later.
|
||||
[open, ..., close] = nested
|
||||
open[0] = open[1] = '('
|
||||
close[0] = close[1] = ')'
|
||||
close.origin = ['', 'end of interpolation', close[2]]
|
||||
|
||||
# Remove leading `'TERMINATOR'` (if any).
|
||||
nested.splice 1, 1 if nested[1]?[0] is 'TERMINATOR'
|
||||
|
||||
unless braceInterpolator
|
||||
# We are not using `{` and `}`, so wrap the interpolated tokens instead.
|
||||
open = @makeToken '(', '(', offsetInChunk, 0
|
||||
close = @makeToken ')', ')', offsetInChunk + index, 0
|
||||
nested = [open, nested..., close]
|
||||
|
||||
# Push a fake `'TOKENS'` token, which will get turned into real tokens later.
|
||||
tokens.push ['TOKENS', nested]
|
||||
|
||||
str = str[index..]
|
||||
offsetInChunk += index
|
||||
|
||||
unless str[...delimiter.length] is delimiter
|
||||
@error "missing #{delimiter}", length: delimiter.length
|
||||
unless str[...closingDelimiter.length] is closingDelimiter
|
||||
@error "missing #{closingDelimiter}", length: delimiter.length
|
||||
|
||||
[firstToken, ..., lastToken] = tokens
|
||||
firstToken[2].first_column -= delimiter.length
|
||||
if lastToken[1].substr(-1) is '\n'
|
||||
lastToken[2].last_line += 1
|
||||
lastToken[2].last_column = delimiter.length - 1
|
||||
lastToken[2].last_column = closingDelimiter.length - 1
|
||||
else
|
||||
lastToken[2].last_column += delimiter.length
|
||||
lastToken[2].last_column += closingDelimiter.length
|
||||
lastToken[2].last_column -= 1 if lastToken[1].length is 0
|
||||
|
||||
{tokens, index: offsetInChunk + delimiter.length}
|
||||
{tokens, index: offsetInChunk + closingDelimiter.length}
|
||||
|
||||
# Merge the array `tokens` of the fake token types `'TOKENS'` and `'NEOSTRING'`
|
||||
# (as returned by `matchWithInterpolations`) into the token stream. The value
|
||||
@@ -973,6 +1073,17 @@ IDENTIFIER = /// ^
|
||||
( [^\n\S]* : (?!:) )? # Is this a property name?
|
||||
///
|
||||
|
||||
CSX_IDENTIFIER = /// ^
|
||||
(?![\d<]) # Must not start with `<`.
|
||||
( (?: (?!\s)[\.\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s and `.`s.
|
||||
///
|
||||
|
||||
CSX_ATTRIBUTE = /// ^
|
||||
(?!\d)
|
||||
( (?: (?!\s)[\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s.
|
||||
( [^\S]* = (?!=) )? # Is this an attribute with a value?
|
||||
///
|
||||
|
||||
NUMBER = ///
|
||||
^ 0b[01]+ | # binary
|
||||
^ 0o[0-7]+ | # octal
|
||||
@@ -1009,6 +1120,17 @@ STRING_DOUBLE = /// ^(?: [^\\"#] | \\[\s\S] | \#(?!\{) )* ///
|
||||
HEREDOC_SINGLE = /// ^(?: [^\\'] | \\[\s\S] | '(?!'') )* ///
|
||||
HEREDOC_DOUBLE = /// ^(?: [^\\"#] | \\[\s\S] | "(?!"") | \#(?!\{) )* ///
|
||||
|
||||
INSIDE_CSX = /// ^(?:
|
||||
[^
|
||||
\{ # Start of CoffeeScript interpolation.
|
||||
< # Maybe CSX tag (`<` not allowed even if bare).
|
||||
]
|
||||
)* /// # Similar to `HEREDOC_DOUBLE` but there is no escaping.
|
||||
CSX_INTERPOLATION = /// ^(?:
|
||||
\{ # CoffeeScript interpolation.
|
||||
| <(?!/) # CSX opening tag.
|
||||
)///
|
||||
|
||||
STRING_OMIT = ///
|
||||
((?:\\\\)+) # Consume (and preserve) an even number of backslashes.
|
||||
| \\[^\S\n]*\n\s* # Remove escaped newlines.
|
||||
@@ -1112,6 +1234,9 @@ INDEXABLE = CALLABLE.concat [
|
||||
'BOOL', 'NULL', 'UNDEFINED', '}', '::'
|
||||
]
|
||||
|
||||
# Tokens which can be the left-hand side of a less-than comparison, i.e. `a<b`.
|
||||
COMPARABLE_LEFT_SIDE = ['IDENTIFIER', ')', ']', 'NUMBER']
|
||||
|
||||
# Tokens which a regular expression will never immediately follow (except spaced
|
||||
# CALLABLEs in some cases), but which a division operator can.
|
||||
#
|
||||
|
||||
325
src/nodes.coffee
325
src/nodes.coffee
@@ -280,7 +280,10 @@ exports.Base = class Base
|
||||
new CodeFragment this, code
|
||||
|
||||
wrapInParentheses: (fragments) ->
|
||||
[].concat @makeCode('('), fragments, @makeCode(')')
|
||||
[@makeCode('('), fragments..., @makeCode(')')]
|
||||
|
||||
wrapInBraces: (fragments) ->
|
||||
[@makeCode('{'), fragments..., @makeCode('}')]
|
||||
|
||||
# `fragmentsList` is an array of arrays of fragments. Each array in fragmentsList will be
|
||||
# concatonated together, with `joinStr` added in between each, to produce a final flat array
|
||||
@@ -534,6 +537,16 @@ exports.NaNLiteral = class NaNLiteral extends NumberLiteral
|
||||
if o.level >= LEVEL_OP then @wrapInParentheses code else code
|
||||
|
||||
exports.StringLiteral = class StringLiteral extends Literal
|
||||
compileNode: (o) ->
|
||||
res = if @csx then [@makeCode @unquote yes] else super()
|
||||
|
||||
unquote: (literal) ->
|
||||
unquoted = @value[1...-1]
|
||||
if literal
|
||||
unquoted.replace /\\n/g, '\n'
|
||||
.replace /\\"/g, '"'
|
||||
else
|
||||
unquoted
|
||||
|
||||
exports.RegexLiteral = class RegexLiteral extends Literal
|
||||
|
||||
@@ -545,6 +558,8 @@ exports.IdentifierLiteral = class IdentifierLiteral extends Literal
|
||||
eachName: (iterator) ->
|
||||
iterator @
|
||||
|
||||
exports.CSXTag = class CSXTag extends IdentifierLiteral
|
||||
|
||||
exports.PropertyName = class PropertyName extends Literal
|
||||
isAssignable: YES
|
||||
|
||||
@@ -778,6 +793,8 @@ exports.Call = class Call extends Base
|
||||
if @variable instanceof Value and @variable.isNotCallable()
|
||||
@variable.error "literal is not a function"
|
||||
|
||||
@csx = @variable.base instanceof CSXTag
|
||||
|
||||
children: ['variable', 'args']
|
||||
|
||||
# When setting the location, we sometimes need to update the start location to
|
||||
@@ -840,6 +857,7 @@ exports.Call = class Call extends Base
|
||||
|
||||
# Compile a vanilla function call.
|
||||
compileNode: (o) ->
|
||||
return @compileCSX o if @csx
|
||||
@variable?.front = @front
|
||||
compiledArgs = []
|
||||
for arg, argIndex in @args
|
||||
@@ -854,6 +872,21 @@ exports.Call = class Call extends Base
|
||||
fragments.push @makeCode('('), compiledArgs..., @makeCode(')')
|
||||
fragments
|
||||
|
||||
compileCSX: (o) ->
|
||||
[attributes, content] = @args
|
||||
attributes.base.csx = yes
|
||||
content?.base.csx = yes
|
||||
fragments = [@makeCode('<')]
|
||||
fragments.push (tag = @variable.compileToFragments(o, LEVEL_ACCESS))...
|
||||
fragments.push attributes.compileToFragments(o, LEVEL_PAREN)...
|
||||
if content
|
||||
fragments.push @makeCode('>')
|
||||
fragments.push content.compileNode(o, LEVEL_LIST)...
|
||||
fragments.push [@makeCode('</'), tag..., @makeCode('>')]...
|
||||
else
|
||||
fragments.push @makeCode(' />')
|
||||
fragments
|
||||
|
||||
#### Super
|
||||
|
||||
# Takes care of converting `super()` calls into calls against the prototype's
|
||||
@@ -1124,27 +1157,49 @@ exports.Obj = class Obj extends Base
|
||||
shouldCache: ->
|
||||
not @isAssignable()
|
||||
|
||||
# Check if object contains splat.
|
||||
hasSplat: ->
|
||||
splat = yes for prop in @properties when prop instanceof Splat
|
||||
splat ? no
|
||||
|
||||
compileNode: (o) ->
|
||||
props = @properties
|
||||
if @generated
|
||||
for node in props when node instanceof Value
|
||||
node.error 'cannot have an implicit value in an implicit object'
|
||||
|
||||
# Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
|
||||
return @compileSpread o if @hasSplat()
|
||||
|
||||
idt = o.indent += TAB
|
||||
lastNoncom = @lastNonComment @properties
|
||||
|
||||
# If this object is the left-hand side of an assignment, all its children
|
||||
# are too.
|
||||
if @lhs
|
||||
for prop in props when prop instanceof Assign
|
||||
{value} = prop
|
||||
unwrappedVal = value.unwrapAll()
|
||||
if unwrappedVal instanceof Arr or unwrappedVal instanceof Obj
|
||||
unwrappedVal.lhs = yes
|
||||
else if unwrappedVal instanceof Assign
|
||||
unwrappedVal.nestedLhs = yes
|
||||
|
||||
isCompact = yes
|
||||
for prop in @properties
|
||||
if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object')
|
||||
if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object' and not @csx)
|
||||
isCompact = no
|
||||
|
||||
answer = []
|
||||
answer.push @makeCode "{#{if isCompact then '' else '\n'}"
|
||||
answer.push @makeCode if isCompact then '' else '\n'
|
||||
for prop, i in props
|
||||
join = if i is props.length - 1
|
||||
''
|
||||
else if isCompact and @csx
|
||||
' '
|
||||
else if isCompact
|
||||
', '
|
||||
else if prop is lastNoncom or prop instanceof Comment
|
||||
else if prop is lastNoncom or prop instanceof Comment or @csx
|
||||
'\n'
|
||||
else
|
||||
',\n'
|
||||
@@ -1157,12 +1212,10 @@ exports.Obj = class Obj extends Base
|
||||
prop.variable
|
||||
else if prop not instanceof Comment
|
||||
prop
|
||||
|
||||
if key instanceof Value and key.hasProperties()
|
||||
key.error 'invalid object key' if prop.context is 'object' or not key.this
|
||||
key = key.properties[0].name
|
||||
prop = new Assign key, prop, 'object'
|
||||
|
||||
if key is prop
|
||||
if prop.shouldCache()
|
||||
[key, value] = prop.base.cache o
|
||||
@@ -1170,11 +1223,13 @@ exports.Obj = class Obj extends Base
|
||||
prop = new Assign key, value, 'object'
|
||||
else if not prop.bareLiteral?(IdentifierLiteral)
|
||||
prop = new Assign prop, prop, 'object'
|
||||
|
||||
if indent then answer.push @makeCode indent
|
||||
prop.csx = yes if @csx
|
||||
answer.push @makeCode ' ' if @csx and i is 0
|
||||
answer.push prop.compileToFragments(o, LEVEL_TOP)...
|
||||
if join then answer.push @makeCode join
|
||||
answer.push @makeCode "#{if isCompact then '' else "\n#{@tab}"}}"
|
||||
answer.push @makeCode if isCompact then '' else "\n#{@tab}"
|
||||
answer = @wrapInBraces answer if not @csx
|
||||
if @front then @wrapInParentheses answer else answer
|
||||
|
||||
assigns: (name) ->
|
||||
@@ -1187,6 +1242,29 @@ exports.Obj = class Obj extends Base
|
||||
prop = prop.unwrapAll()
|
||||
prop.eachName iterator if prop.eachName?
|
||||
|
||||
# Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
|
||||
# `obj2 = {a: 1, obj..., c: 3, d: 4}` → `obj2 = Object.assign({}, {a: 1}, obj, {c: 3, d: 4})`
|
||||
compileSpread: (o) ->
|
||||
props = @properties
|
||||
# Store object spreads.
|
||||
splatSlice = []
|
||||
propSlices = []
|
||||
slices = []
|
||||
addSlice = ->
|
||||
slices.push new Obj propSlices if propSlices.length
|
||||
slices.push splatSlice... if splatSlice.length
|
||||
splatSlice = []
|
||||
propSlices = []
|
||||
for prop in props
|
||||
if prop instanceof Splat
|
||||
splatSlice.push new Value prop.name
|
||||
addSlice()
|
||||
else
|
||||
propSlices.push prop
|
||||
addSlice()
|
||||
slices.unshift new Obj unless slices[0] instanceof Obj
|
||||
(new Call new Literal('Object.assign'), slices).compileToFragments o
|
||||
|
||||
#### Arr
|
||||
|
||||
# An array literal.
|
||||
@@ -1262,26 +1340,33 @@ exports.Class = class Class extends Base
|
||||
parentName = @parent.base.value if @parent instanceof Value and not @parent.hasProperties()
|
||||
@hasNameClash = @name? and @name is parentName
|
||||
|
||||
if executableBody or @hasNameClash
|
||||
@compileNode = @compileClassDeclaration
|
||||
result = new ExecutableClassBody(@, executableBody).compileToFragments o
|
||||
@compileNode = @constructor::compileNode
|
||||
else
|
||||
result = @compileClassDeclaration o
|
||||
node = @
|
||||
|
||||
if executableBody or @hasNameClash
|
||||
node = new ExecutableClassBody node, executableBody
|
||||
else if not @name? and o.level is LEVEL_TOP
|
||||
# Anonymous classes are only valid in expressions
|
||||
result = @wrapInParentheses result if not @name? and o.level is LEVEL_TOP
|
||||
node = new Parens node
|
||||
|
||||
if @boundMethods.length and @parent
|
||||
@variable ?= new IdentifierLiteral o.scope.freeVariable '_class'
|
||||
[@variable, @variableRef] = @variable.cache o unless @variableRef?
|
||||
|
||||
if @variable
|
||||
assign = new Assign @variable, new Literal(''), null, { @moduleDeclaration }
|
||||
[ assign.compileToFragments(o)..., result... ]
|
||||
else
|
||||
result
|
||||
node = new Assign @variable, node, null, { @moduleDeclaration }
|
||||
|
||||
@compileNode = @compileClassDeclaration
|
||||
try
|
||||
return node.compileToFragments o
|
||||
finally
|
||||
delete @compileNode
|
||||
|
||||
compileClassDeclaration: (o) ->
|
||||
@ctor ?= @makeDefaultConstructor() if @externalCtor
|
||||
@ctor ?= @makeDefaultConstructor() if @externalCtor or @boundMethods.length
|
||||
@ctor?.noReturn = true
|
||||
|
||||
@proxyBoundMethods() if @boundMethods.length
|
||||
|
||||
o.indent += TAB
|
||||
|
||||
result = []
|
||||
@@ -1317,6 +1402,7 @@ exports.Class = class Class extends Base
|
||||
|
||||
walkBody: ->
|
||||
@ctor = null
|
||||
@boundMethods = []
|
||||
executableBody = null
|
||||
|
||||
initializer = []
|
||||
@@ -1362,6 +1448,8 @@ exports.Class = class Class extends Base
|
||||
@ctor = method
|
||||
else if method.isStatic and method.bound
|
||||
method.context = @name
|
||||
else if method.bound
|
||||
@boundMethods.push method
|
||||
|
||||
if initializer.length isnt expressions.length
|
||||
@body.expressions = (expression.hoist() for expression in initializer)
|
||||
@@ -1399,7 +1487,7 @@ exports.Class = class Class extends Base
|
||||
method.name = new (if methodName.shouldCache() then Index else Access) methodName
|
||||
method.name.updateLocationDataIfMissing methodName.locationData
|
||||
method.ctor = (if @parent then 'derived' else 'base') if methodName.value is 'constructor'
|
||||
method.error 'Methods cannot be bound functions' if method.bound
|
||||
method.error 'Cannot define a constructor as a bound (fat arrow) function' if method.bound and method.ctor
|
||||
|
||||
method
|
||||
|
||||
@@ -1418,6 +1506,15 @@ exports.Class = class Class extends Base
|
||||
|
||||
ctor
|
||||
|
||||
proxyBoundMethods: ->
|
||||
@ctor.thisAssignments = for method in @boundMethods
|
||||
method.classVariable = @variableRef if @parent
|
||||
|
||||
name = new Value(new ThisLiteral, [ method.name ])
|
||||
new Assign name, new Call(new Value(name, [new Access new PropertyName 'bind']), [new ThisLiteral])
|
||||
|
||||
null
|
||||
|
||||
exports.ExecutableClassBody = class ExecutableClassBody extends Base
|
||||
children: [ 'class', 'body' ]
|
||||
|
||||
@@ -1501,7 +1598,7 @@ exports.ExecutableClassBody = class ExecutableClassBody extends Base
|
||||
@body.traverseChildren false, (node) =>
|
||||
if node instanceof ThisLiteral
|
||||
node.value = @name
|
||||
else if node instanceof Code and node.bound
|
||||
else if node instanceof Code and node.bound and node.isStatic
|
||||
node.context = @name
|
||||
|
||||
# Make class/prototype assignments for invalid ES properties
|
||||
@@ -1728,6 +1825,9 @@ exports.Assign = class Assign extends Base
|
||||
# destructured variables.
|
||||
@variable.base.lhs = yes
|
||||
return @compileDestructuring o unless @variable.isAssignable()
|
||||
# Object destructuring. Can be removed once ES proposal hits Stage 4.
|
||||
return @compileObjectDestruct(o) if @variable.isObject() and @variable.contains (node) ->
|
||||
node instanceof Obj and node.hasSplat()
|
||||
|
||||
return @compileSplice o if @variable.isSplice()
|
||||
return @compileConditional o if @context in ['||=', '&&=', '?=']
|
||||
@@ -1758,6 +1858,7 @@ exports.Assign = class Assign extends Base
|
||||
[properties..., prototype, name] = @variable.properties
|
||||
@value.name = name if prototype.name?.value is 'prototype'
|
||||
|
||||
@value.base.csxAttribute = yes if @csx
|
||||
val = @value.compileToFragments o, LEVEL_LIST
|
||||
compiledName = @variable.compileToFragments o, LEVEL_LIST
|
||||
|
||||
@@ -1765,16 +1866,110 @@ exports.Assign = class Assign extends Base
|
||||
if @variable.shouldCache()
|
||||
compiledName.unshift @makeCode '['
|
||||
compiledName.push @makeCode ']'
|
||||
return compiledName.concat @makeCode(": "), val
|
||||
return compiledName.concat @makeCode(if @csx then '=' else ': '), val
|
||||
|
||||
answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
|
||||
# Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration,
|
||||
# if we’re destructuring without declaring, the destructuring assignment must be wrapped in parentheses.
|
||||
if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj and not @param)
|
||||
if o.level > LEVEL_LIST or (o.level is LEVEL_TOP and isValue and @variable.base instanceof Obj and not @nestedLhs and not @param)
|
||||
@wrapInParentheses answer
|
||||
else
|
||||
answer
|
||||
|
||||
# Check object destructuring variable for rest elements;
|
||||
# can be removed once ES proposal hits Stage 4.
|
||||
compileObjectDestruct: (o) ->
|
||||
# Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration,
|
||||
# if we’re destructuring without declaring, the destructuring assignment
|
||||
# must be wrapped in parentheses: `({a, b} = obj)`. Helper function
|
||||
# `setScopeVar()` declares variables `a` and `b` at the top of the
|
||||
# current scope.
|
||||
setScopeVar = (prop) ->
|
||||
newVar = false
|
||||
return if prop instanceof Assign and prop.value.base instanceof Obj
|
||||
if prop instanceof Assign
|
||||
if prop.value.base instanceof IdentifierLiteral
|
||||
newVar = prop.value.base.compile o
|
||||
else
|
||||
newVar = prop.variable.base.compile o
|
||||
else
|
||||
newVar = prop.compile o
|
||||
o.scope.add(newVar, 'var', true) if newVar
|
||||
|
||||
# Returns a safe (cached) reference to the key for a given property
|
||||
getPropKey = (prop) ->
|
||||
if prop instanceof Assign
|
||||
[prop.variable, key] = prop.variable.cache o
|
||||
key
|
||||
else
|
||||
prop
|
||||
|
||||
# Returns the name of a given property for use with excludeProps
|
||||
# Property names are quoted (e.g. `a: b` -> 'a'), and everything else uses the key reference
|
||||
# (e.g. `'a': b -> 'a'`, `"#{a}": b` -> <cached>`)
|
||||
getPropName = (prop) ->
|
||||
key = getPropKey prop
|
||||
cached = prop instanceof Assign and prop.variable != key
|
||||
if cached or not key.isAssignable()
|
||||
key
|
||||
else
|
||||
new Literal "'#{key.compile o}'"
|
||||
|
||||
# Recursive function for searching and storing rest elements in objects.
|
||||
# e.g. `{[properties...]} = source`.
|
||||
traverseRest = (properties, source) =>
|
||||
restElements = []
|
||||
restIndex = undefined
|
||||
|
||||
for prop, index in properties
|
||||
setScopeVar prop.unwrap()
|
||||
if prop instanceof Assign
|
||||
# prop is `k: expr`, we need to check `expr` for nested splats
|
||||
if prop.value.isObject?()
|
||||
# prop is `k: {...}`
|
||||
nestedProperties = prop.value.base.properties
|
||||
else if prop.value instanceof Assign and prop.value.variable.isObject()
|
||||
# prop is `k: {...} = default`
|
||||
nestedProperties = prop.value.variable.base.properties
|
||||
[prop.value.value, nestedSourceDefault] = prop.value.value.cache o
|
||||
if nestedProperties
|
||||
nestedSource = new Value source.base, source.properties.concat [new Access getPropKey prop]
|
||||
nestedSource = new Value new Op '?', nestedSource, nestedSourceDefault if nestedSourceDefault
|
||||
restElements = restElements.concat traverseRest nestedProperties, nestedSource
|
||||
else if prop instanceof Splat
|
||||
prop.error "multiple rest elements are disallowed in object destructuring" if restIndex?
|
||||
restIndex = index
|
||||
restElements.push {
|
||||
name: prop.name.unwrapAll()
|
||||
source
|
||||
excludeProps: new Arr (getPropName p for p in properties when p isnt prop)
|
||||
}
|
||||
|
||||
if restIndex?
|
||||
# Remove rest element from the properties after iteration
|
||||
properties.splice restIndex, 1
|
||||
|
||||
restElements
|
||||
|
||||
# Cache the value for reuse with rest elements
|
||||
[@value, valueRef] = @value.cache o
|
||||
|
||||
# Find all rest elements.
|
||||
restElements = traverseRest @variable.base.properties, valueRef
|
||||
|
||||
result = new Block [@]
|
||||
for restElement in restElements
|
||||
value = new Call new Value(new Literal utility 'objectWithoutKeys', o), [restElement.source, restElement.excludeProps]
|
||||
result.push new Assign restElement.name, value
|
||||
|
||||
fragments = result.compileToFragments o
|
||||
if o.level is LEVEL_TOP
|
||||
# Remove leading tab and trailing semicolon
|
||||
fragments.shift()
|
||||
fragments.pop()
|
||||
|
||||
fragments
|
||||
|
||||
# Brief implementation of recursive pattern matching, when assigning array or
|
||||
# object literals to a value. Peeks at their properties to assign inner names.
|
||||
compileDestructuring: (o) ->
|
||||
@@ -1838,7 +2033,7 @@ exports.Assign = class Assign extends Base
|
||||
|
||||
# At this point, there are several things to destructure. So the `fn()` in
|
||||
# `{a, b} = fn()` must be cached, for example. Make vvar into a simple
|
||||
# variable if it isn't already.
|
||||
# variable if it isn’t already.
|
||||
if value.unwrap() not instanceof IdentifierLiteral or @variable.assigns(vvarText)
|
||||
ref = o.scope.freeVariable 'ref'
|
||||
assigns.push [@makeCode(ref + ' = '), vvar...]
|
||||
@@ -2029,7 +2224,6 @@ exports.Code = class Code extends Base
|
||||
@eachParamName (name, node, param) ->
|
||||
node.error "multiple parameters named '#{name}'" if name in paramNames
|
||||
paramNames.push name
|
||||
|
||||
if node.this
|
||||
name = node.properties[0].name.value
|
||||
name = "_#{name}" if name in JS_FORBIDDEN
|
||||
@@ -2054,7 +2248,6 @@ exports.Code = class Code extends Base
|
||||
param.error 'only one splat or expansion parameter is allowed per function definition'
|
||||
else if param instanceof Expansion and @params.length is 1
|
||||
param.error 'an expansion parameter cannot be the only parameter in a function definition'
|
||||
|
||||
haveSplatParam = yes
|
||||
if param.splat
|
||||
if param.name instanceof Arr
|
||||
@@ -2063,12 +2256,12 @@ exports.Code = class Code extends Base
|
||||
# function parameter list, and if so, how?
|
||||
splatParamName = o.scope.freeVariable 'arg'
|
||||
params.push ref = new Value new IdentifierLiteral splatParamName
|
||||
exprs.push new Assign new Value(param.name), ref, null, param: yes
|
||||
exprs.push new Assign new Value(param.name), ref
|
||||
else
|
||||
params.push ref = param.asReference o
|
||||
splatParamName = fragmentsToText ref.compileNode o
|
||||
if param.shouldCache()
|
||||
exprs.push new Assign new Value(param.name), ref, null, param: yes
|
||||
exprs.push new Assign new Value(param.name), ref
|
||||
else # `param` is an Expansion
|
||||
splatParamName = o.scope.freeVariable 'args'
|
||||
params.push new Value new IdentifierLiteral splatParamName
|
||||
@@ -2088,10 +2281,10 @@ exports.Code = class Code extends Base
|
||||
# `(arg) => { var a = arg.a; }`, with a default value if it has one.
|
||||
if param.value?
|
||||
condition = new Op '===', param, new UndefinedLiteral
|
||||
ifTrue = new Assign new Value(param.name), param.value, null, param: yes
|
||||
ifTrue = new Assign new Value(param.name), param.value
|
||||
exprs.push new If condition, ifTrue
|
||||
else
|
||||
exprs.push new Assign new Value(param.name), param.asReference(o), null, param: yes
|
||||
exprs.push new Assign new Value(param.name), param.asReference(o)
|
||||
|
||||
# If this parameter comes before the splat or expansion, it will go
|
||||
# in the function definition parameter list.
|
||||
@@ -2113,6 +2306,16 @@ exports.Code = class Code extends Base
|
||||
param.name.lhs = yes
|
||||
param.name.eachName (prop) ->
|
||||
o.scope.parameter prop.value
|
||||
# Compile `foo({a, b...}) ->` to `foo(arg) -> {a, b...} = arg`.
|
||||
# Can be removed once ES proposal hits Stage 4.
|
||||
if param.name instanceof Obj and param.name.hasSplat()
|
||||
splatParamName = o.scope.freeVariable 'arg'
|
||||
o.scope.parameter splatParamName
|
||||
ref = new Value new IdentifierLiteral splatParamName
|
||||
exprs.push new Assign new Value(param.name), ref
|
||||
# Compile `foo({a, b...} = {}) ->` to `foo(arg = {}) -> {a, b...} = arg`.
|
||||
if param.value? and not param.assignedInBody
|
||||
ref = new Assign ref, param.value, null, param: yes
|
||||
else
|
||||
o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o
|
||||
params.push ref
|
||||
@@ -2125,7 +2328,8 @@ exports.Code = class Code extends Base
|
||||
condition = new Op '===', param, new UndefinedLiteral
|
||||
ifTrue = new Assign new Value(param.name), param.value
|
||||
exprs.push new If condition, ifTrue
|
||||
# Add this parameter to the scope, since it wouldn’t have been added yet since it was skipped earlier.
|
||||
# Add this parameter to the scope, since it wouldn’t have been added
|
||||
# yet since it was skipped earlier.
|
||||
o.scope.add param.name.value, 'var', yes if param.name?.value?
|
||||
|
||||
# If there were parameters after the splat or expansion parameter, those
|
||||
@@ -2140,6 +2344,9 @@ exports.Code = class Code extends Base
|
||||
wasEmpty = @body.isEmpty()
|
||||
@body.expressions.unshift thisAssignments... unless @expandCtorSuper thisAssignments
|
||||
@body.expressions.unshift exprs...
|
||||
if @isMethod and @bound and not @isStatic and @classVariable
|
||||
boundMethodCheck = new Value new Literal utility 'boundMethodCheck', o
|
||||
@body.expressions.unshift new Call(boundMethodCheck, [new Value(new ThisLiteral), @classVariable])
|
||||
@body.makeReturn() unless wasEmpty or @noReturn
|
||||
|
||||
# Assemble the output
|
||||
@@ -2773,13 +2980,14 @@ exports.Parens = class Parens extends Base
|
||||
|
||||
compileNode: (o) ->
|
||||
expr = @body.unwrap()
|
||||
if expr instanceof Value and expr.isAtomic()
|
||||
if expr instanceof Value and expr.isAtomic() and not @csxAttribute
|
||||
expr.front = @front
|
||||
return expr.compileToFragments o
|
||||
fragments = expr.compileToFragments o, LEVEL_PAREN
|
||||
bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call or
|
||||
(expr instanceof For and expr.returns)) and (o.level < LEVEL_COND or
|
||||
fragments.length <= 3)
|
||||
return @wrapInBraces fragments if @csxAttribute
|
||||
if bare then fragments else @wrapInParentheses fragments
|
||||
|
||||
#### StringWithInterpolations
|
||||
@@ -2798,6 +3006,11 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
|
||||
shouldCache: -> @body.shouldCache()
|
||||
|
||||
compileNode: (o) ->
|
||||
if @csxAttribute
|
||||
wrapped = new Parens new StringWithInterpolations @body
|
||||
wrapped.csxAttribute = yes
|
||||
return wrapped.compileNode o
|
||||
|
||||
# Assumes that `expr` is `Value` » `StringLiteral` or `Op`
|
||||
expr = @body.unwrap()
|
||||
|
||||
@@ -2812,25 +3025,31 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
|
||||
return yes
|
||||
|
||||
fragments = []
|
||||
fragments.push @makeCode '`'
|
||||
fragments.push @makeCode '`' unless @csx
|
||||
for element in elements
|
||||
if element instanceof StringLiteral
|
||||
value = element.value[1...-1]
|
||||
# Backticks and `${` inside template literals must be escaped.
|
||||
value = value.replace /(\\*)(`|\$\{)/g, (match, backslashes, toBeEscaped) ->
|
||||
if backslashes.length % 2 is 0
|
||||
"#{backslashes}\\#{toBeEscaped}"
|
||||
else
|
||||
match
|
||||
value = element.unquote @csx
|
||||
unless @csx
|
||||
# Backticks and `${` inside template literals must be escaped.
|
||||
value = value.replace /(\\*)(`|\$\{)/g, (match, backslashes, toBeEscaped) ->
|
||||
if backslashes.length % 2 is 0
|
||||
"#{backslashes}\\#{toBeEscaped}"
|
||||
else
|
||||
match
|
||||
fragments.push @makeCode value
|
||||
else
|
||||
fragments.push @makeCode '${'
|
||||
fragments.push element.compileToFragments(o, LEVEL_PAREN)...
|
||||
fragments.push @makeCode '}'
|
||||
fragments.push @makeCode '`'
|
||||
|
||||
fragments.push @makeCode '$' unless @csx
|
||||
code = element.compileToFragments(o, LEVEL_PAREN)
|
||||
code = @wrapInBraces code unless @isNestedTag element
|
||||
fragments.push code...
|
||||
fragments.push @makeCode '`' unless @csx
|
||||
fragments
|
||||
|
||||
isNestedTag: (element) ->
|
||||
exprs = element?.body?.expressions
|
||||
call = exprs?[0]
|
||||
@csx and exprs and exprs.length is 1 and call instanceof Call and call.csx
|
||||
|
||||
#### For
|
||||
|
||||
# CoffeeScript's replacement for the *for* loop is our array and object
|
||||
@@ -3097,6 +3316,20 @@ exports.If = class If extends Base
|
||||
|
||||
UTILITIES =
|
||||
modulo: -> 'function(a, b) { return (+a % (b = +b) + b) % b; }'
|
||||
objectWithoutKeys: -> "
|
||||
function(o, ks) {
|
||||
var res = {};
|
||||
for (var k in o) ([].indexOf.call(ks, k) < 0 && {}.hasOwnProperty.call(o, k)) && (res[k] = o[k]);
|
||||
return res;
|
||||
}
|
||||
"
|
||||
boundMethodCheck: -> "
|
||||
function(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new Error('Bound instance method accessed before binding');
|
||||
}
|
||||
}
|
||||
"
|
||||
|
||||
# Shortcuts to speed up the lookup time for native functions.
|
||||
hasProp: -> '{}.hasOwnProperty'
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#
|
||||
# The first non-option is considered to be the start of the file (and file
|
||||
# option) list, and all subsequent arguments are left unparsed.
|
||||
#
|
||||
# The `coffee` command uses an instance of **OptionParser** to parse its
|
||||
# command-line arguments in `src/command.coffee`.
|
||||
exports.OptionParser = class OptionParser
|
||||
|
||||
# Initialize with a list of valid options, in the form:
|
||||
|
||||
@@ -35,7 +35,7 @@ replDefaults =
|
||||
ast = CoffeeScript.nodes tokens
|
||||
# Add assignment to `_` variable to force the input to be an expression.
|
||||
ast = new Block [
|
||||
new Assign (new Value new Literal '_'), ast, '='
|
||||
new Assign (new Value new Literal '__'), ast, '='
|
||||
]
|
||||
js = ast.compile {bare: yes, locals: Object.keys(context), referencedVars}
|
||||
cb null, runInContext js, context, filename
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
# shorthand into the unambiguous long form, add implicit indentation and
|
||||
# parentheses, and generally clean things up.
|
||||
|
||||
{throwSyntaxError} = require './helpers'
|
||||
|
||||
# Create a generated token: one that exists due to a use of implicit syntax.
|
||||
generate = (tag, value, origin) ->
|
||||
tok = [tag, value]
|
||||
@@ -31,6 +33,7 @@ exports.Rewriter = class Rewriter
|
||||
@tagPostfixConditionals()
|
||||
@addImplicitBracesAndParens()
|
||||
@addLocationDataToGeneratedTokens()
|
||||
@enforceValidCSXAttributes()
|
||||
@fixOutdentLocationData()
|
||||
@tokens
|
||||
|
||||
@@ -45,16 +48,18 @@ exports.Rewriter = class Rewriter
|
||||
i += block.call this, token, i, tokens while token = tokens[i]
|
||||
true
|
||||
|
||||
detectEnd: (i, condition, action) ->
|
||||
detectEnd: (i, condition, action, opts = {}) ->
|
||||
{tokens} = this
|
||||
levels = 0
|
||||
while token = tokens[i]
|
||||
return action.call this, token, i if levels is 0 and condition.call this, token, i
|
||||
return action.call this, token, i - 1 if not token or levels < 0
|
||||
return action.call this, token, i if levels is 0 and condition.call this, token, i
|
||||
if token[0] in EXPRESSION_START
|
||||
levels += 1
|
||||
else if token[0] in EXPRESSION_END
|
||||
levels -= 1
|
||||
if levels < 0
|
||||
return if opts.returnOnNegativeLevel
|
||||
return action.call this, token, i
|
||||
i += 1
|
||||
i - 1
|
||||
|
||||
@@ -65,21 +70,19 @@ exports.Rewriter = class Rewriter
|
||||
@tokens.splice 0, i if i
|
||||
|
||||
# The lexer has tagged the opening parenthesis of a method call. Match it with
|
||||
# its paired close. We have the mis-nested outdent case included here for
|
||||
# calls that close on the same line, just before their outdent.
|
||||
# its paired close.
|
||||
closeOpenCalls: ->
|
||||
condition = (token, i) ->
|
||||
token[0] in [')', 'CALL_END'] or
|
||||
token[0] is 'OUTDENT' and @tag(i - 1) is ')'
|
||||
token[0] in [')', 'CALL_END']
|
||||
|
||||
action = (token, i) ->
|
||||
@tokens[if token[0] is 'OUTDENT' then i - 1 else i][0] = 'CALL_END'
|
||||
token[0] = 'CALL_END'
|
||||
|
||||
@scanTokens (token, i) ->
|
||||
@detectEnd i + 1, condition, action if token[0] is 'CALL_START'
|
||||
1
|
||||
|
||||
# The lexer has tagged the opening parenthesis of an indexing operation call.
|
||||
# The lexer has tagged the opening bracket of an indexing operation call.
|
||||
# Match it with its paired close.
|
||||
closeOpenIndexes: ->
|
||||
condition = (token, i) ->
|
||||
@@ -140,7 +143,7 @@ exports.Rewriter = class Rewriter
|
||||
@scanTokens (token, i, tokens) ->
|
||||
[tag] = token
|
||||
[prevTag] = prevToken = if i > 0 then tokens[i - 1] else []
|
||||
[nextTag] = if i < tokens.length - 1 then tokens[i + 1] else []
|
||||
[nextTag] = nextToken = if i < tokens.length - 1 then tokens[i + 1] else []
|
||||
stackTop = -> stack[stack.length - 1]
|
||||
startIdx = i
|
||||
|
||||
@@ -159,24 +162,20 @@ exports.Rewriter = class Rewriter
|
||||
# class declaration or if-conditionals)
|
||||
inImplicitControl = -> inImplicit() and stackTop()?[0] is 'CONTROL'
|
||||
|
||||
startImplicitCall = (j) ->
|
||||
idx = j ? i
|
||||
startImplicitCall = (idx) ->
|
||||
stack.push ['(', idx, ours: yes]
|
||||
tokens.splice idx, 0, generate 'CALL_START', '('
|
||||
i += 1 if not j?
|
||||
|
||||
endImplicitCall = ->
|
||||
stack.pop()
|
||||
tokens.splice i, 0, generate 'CALL_END', ')', ['', 'end of input', token[2]]
|
||||
i += 1
|
||||
|
||||
startImplicitObject = (j, startsLine = yes) ->
|
||||
idx = j ? i
|
||||
startImplicitObject = (idx, startsLine = yes) ->
|
||||
stack.push ['{', idx, sameLine: yes, startsLine: startsLine, ours: yes]
|
||||
val = new String '{'
|
||||
val.generated = yes
|
||||
tokens.splice idx, 0, generate '{', val, token
|
||||
i += 1 if not j?
|
||||
|
||||
endImplicitObject = (j) ->
|
||||
j = j ? i
|
||||
@@ -184,9 +183,20 @@ exports.Rewriter = class Rewriter
|
||||
tokens.splice j, 0, generate '}', '}', token
|
||||
i += 1
|
||||
|
||||
# Don't end an implicit call on next indent if any of these are in an argument
|
||||
if inImplicitCall() and tag in ['IF', 'TRY', 'FINALLY', 'CATCH',
|
||||
'CLASS', 'SWITCH']
|
||||
implicitObjectContinues = (j) =>
|
||||
nextTerminatorIdx = null
|
||||
@detectEnd j,
|
||||
(token) -> token[0] is 'TERMINATOR'
|
||||
(token, i) -> nextTerminatorIdx = i
|
||||
returnOnNegativeLevel: yes
|
||||
return no unless nextTerminatorIdx?
|
||||
@looksObjectish nextTerminatorIdx + 1
|
||||
|
||||
# Don't end an implicit call/object on next indent if any of these are in an argument/value
|
||||
if (
|
||||
(inImplicitCall() or inImplicitObject()) and tag in CONTROL_IN_IMPLICIT or
|
||||
inImplicitObject() and prevTag is ':' and tag is 'FOR'
|
||||
)
|
||||
stack.push ['CONTROL', i, ours: yes]
|
||||
return forward(1)
|
||||
|
||||
@@ -197,8 +207,12 @@ exports.Rewriter = class Rewriter
|
||||
# 1. We have seen a `CONTROL` argument on the line.
|
||||
# 2. The last token before the indent is part of the list below
|
||||
#
|
||||
if prevTag not in ['=>', '->', '[', '(', ',', '{', 'TRY', 'ELSE', '=']
|
||||
endImplicitCall() while inImplicitCall()
|
||||
if prevTag not in ['=>', '->', '[', '(', ',', '{', 'ELSE', '=']
|
||||
while inImplicitCall() or inImplicitObject() and prevTag isnt ':'
|
||||
if inImplicitCall()
|
||||
endImplicitCall()
|
||||
else
|
||||
endImplicitObject()
|
||||
stack.pop() if inImplicitControl()
|
||||
stack.push [tag, i]
|
||||
return forward(1)
|
||||
@@ -225,7 +239,7 @@ exports.Rewriter = class Rewriter
|
||||
tag is '?' and i > 0 and not tokens[i - 1].spaced) and
|
||||
(nextTag in IMPLICIT_CALL or
|
||||
nextTag in IMPLICIT_UNSPACED_CALL and
|
||||
not tokens[i + 1]?.spaced and not tokens[i + 1]?.newLine)
|
||||
not nextToken.spaced and not nextToken.newLine)
|
||||
tag = token[0] = 'FUNC_EXIST' if tag is '?'
|
||||
startImplicitCall i + 1
|
||||
return forward(2)
|
||||
@@ -236,13 +250,6 @@ exports.Rewriter = class Rewriter
|
||||
# a: b
|
||||
# c: d
|
||||
#
|
||||
# and
|
||||
#
|
||||
# f
|
||||
# 1
|
||||
# a: b
|
||||
# b: c
|
||||
#
|
||||
# Don't accept implicit calls of this type, when on the same line
|
||||
# as the control structures below as that may misinterpret constructs like:
|
||||
#
|
||||
@@ -272,9 +279,6 @@ exports.Rewriter = class Rewriter
|
||||
else i - 1
|
||||
s -= 2 while @tag(s - 2) is 'HERECOMMENT'
|
||||
|
||||
# Mark if the value is a for loop
|
||||
@insideForDeclaration = nextTag is 'FOR'
|
||||
|
||||
startsLine = s is 0 or @tag(s - 1) in LINEBREAKS or tokens[s - 1].newLine
|
||||
# Are we just continuing an already declared object?
|
||||
if stackTop()
|
||||
@@ -315,8 +319,9 @@ exports.Rewriter = class Rewriter
|
||||
endImplicitCall()
|
||||
# Close implicit objects such as:
|
||||
# return a: 1, b: 2 unless true
|
||||
else if inImplicitObject() and not @insideForDeclaration and sameLine and
|
||||
tag isnt 'TERMINATOR' and prevTag isnt ':'
|
||||
else if inImplicitObject() and sameLine and
|
||||
tag isnt 'TERMINATOR' and prevTag isnt ':' and
|
||||
not (tag in ['POST_IF', 'FOR', 'WHILE', 'UNTIL'] and startsLine and implicitObjectContinues(i + 1))
|
||||
endImplicitObject()
|
||||
# Close implicit objects when at end of line, line didn't end with a comma
|
||||
# and the implicit object didn't start the line or the next line doesn't look like
|
||||
@@ -342,7 +347,6 @@ exports.Rewriter = class Rewriter
|
||||
# f a, b: c, d: e, f, g: h: i, j
|
||||
#
|
||||
if tag is ',' and not @looksObjectish(i + 1) and inImplicitObject() and
|
||||
not @insideForDeclaration and
|
||||
(nextTag isnt 'TERMINATOR' or not @looksObjectish(i + 2))
|
||||
# When nextTag is OUTDENT the comma is insignificant and
|
||||
# should just be ignored so embed it in the implicit object.
|
||||
@@ -355,6 +359,15 @@ exports.Rewriter = class Rewriter
|
||||
endImplicitObject i + offset
|
||||
return forward(1)
|
||||
|
||||
# Make sure only strings and wrapped expressions are used in CSX attributes
|
||||
enforceValidCSXAttributes: ->
|
||||
@scanTokens (token, i, tokens) ->
|
||||
if token.csxColon
|
||||
next = tokens[i + 1]
|
||||
if next[0] not in ['STRING_START', 'STRING', '(']
|
||||
throwSyntaxError 'expected wrapped or quoted CSX attribute', next[2]
|
||||
return 1
|
||||
|
||||
# Add location data to all tokens generated by the rewriter.
|
||||
addLocationDataToGeneratedTokens: ->
|
||||
@scanTokens (token, i, tokens) ->
|
||||
@@ -421,6 +434,10 @@ exports.Rewriter = class Rewriter
|
||||
for j in [1..2] when @tag(i + j) in ['OUTDENT', 'TERMINATOR', 'FINALLY']
|
||||
tokens.splice i + j, 0, @indentation()...
|
||||
return 2 + j
|
||||
if tag in ['->', '=>'] and (@tag(i + 1) is ',' or @tag(i + 1) is '.' and token.newLine)
|
||||
[indent, outdent] = @indentation tokens[i]
|
||||
tokens.splice i + 1, 0, indent, outdent
|
||||
return 1
|
||||
if tag in SINGLE_LINERS and @tag(i + 1) isnt 'INDENT' and
|
||||
not (tag is 'ELSE' and @tag(i + 1) is 'IF')
|
||||
starter = tag
|
||||
@@ -505,7 +522,7 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'PROPERTY', 'SUPER', ')', 'CALL_END', ']', 'IN
|
||||
|
||||
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
||||
IMPLICIT_CALL = [
|
||||
'IDENTIFIER', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN'
|
||||
'IDENTIFIER', 'CSX_TAG', 'PROPERTY', 'NUMBER', 'INFINITY', 'NAN'
|
||||
'STRING', 'STRING_START', 'REGEX', 'REGEX_START', 'JS'
|
||||
'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS'
|
||||
'UNDEFINED', 'NULL', 'BOOL'
|
||||
@@ -529,3 +546,6 @@ LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT']
|
||||
|
||||
# Tokens that close open calls when they follow a newline.
|
||||
CALL_CLOSERS = ['.', '?.', '::', '?::']
|
||||
|
||||
# Tokens that prevent a subsequent indent from ending implicit calls/objects
|
||||
CONTROL_IN_IMPLICIT = ['IF', 'TRY', 'FINALLY', 'CATCH', 'CLASS', 'SWITCH']
|
||||
|
||||
75
test/argument-parsing.coffee
Normal file
75
test/argument-parsing.coffee
Normal file
@@ -0,0 +1,75 @@
|
||||
return unless require?
|
||||
|
||||
{buildCSOptionParser} = require '../lib/coffeescript/command'
|
||||
|
||||
optionParser = buildCSOptionParser()
|
||||
|
||||
sameOptions = (opts1, opts2, msg) ->
|
||||
ownKeys = Object.keys(opts1).sort()
|
||||
otherKeys = Object.keys(opts2).sort()
|
||||
arrayEq ownKeys, otherKeys, msg
|
||||
for k in ownKeys
|
||||
arrayEq opts1[k], opts2[k], msg
|
||||
yes
|
||||
|
||||
test "combined options are still split after initial file name", ->
|
||||
argv = ['some-file.coffee', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected = arguments: ['some-file.coffee', '-b', '-c']
|
||||
sameOptions parsed, expected
|
||||
|
||||
argv = ['some-file.litcoffee', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected = arguments: ['some-file.litcoffee', '-b', '-c']
|
||||
sameOptions parsed, expected
|
||||
|
||||
argv = ['-c', 'some-file.coffee', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected =
|
||||
compile: yes
|
||||
arguments: ['some-file.coffee', '-b', '-c']
|
||||
sameOptions parsed, expected
|
||||
|
||||
argv = ['-bc', 'some-file.coffee', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected =
|
||||
bare: yes
|
||||
compile: yes
|
||||
arguments: ['some-file.coffee', '-b', '-c']
|
||||
sameOptions parsed, expected
|
||||
|
||||
test "combined options are not split after a '--'", ->
|
||||
argv = ['--', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected = arguments: ['-bc']
|
||||
sameOptions parsed, expected
|
||||
|
||||
argv = ['-bc', '--', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected =
|
||||
bare: yes
|
||||
compile: yes
|
||||
arguments: ['-bc']
|
||||
sameOptions parsed, expected
|
||||
|
||||
test "options are not split after any '--'", ->
|
||||
argv = ['--', '--', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected = arguments: ['--', '-bc']
|
||||
sameOptions parsed, expected
|
||||
|
||||
argv = ['--', 'some-file.coffee', '--', 'arg']
|
||||
parsed = optionParser.parse argv
|
||||
expected = arguments: ['some-file.coffee', '--', 'arg']
|
||||
sameOptions parsed, expected
|
||||
|
||||
argv = ['--', 'arg', 'some-file.coffee', '--', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected = arguments: ['arg', 'some-file.coffee', '--', '-bc']
|
||||
sameOptions parsed, expected
|
||||
|
||||
test "later '--' are removed", ->
|
||||
argv = ['some-file.coffee', '--', '-bc']
|
||||
parsed = optionParser.parse argv
|
||||
expected = arguments: ['some-file.coffee', '-bc']
|
||||
sameOptions parsed, expected
|
||||
@@ -235,6 +235,154 @@ test "destructuring assignment against an expression", ->
|
||||
eq a, y
|
||||
eq b, z
|
||||
|
||||
test "destructuring assignment with objects and splats: ES2015", ->
|
||||
obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
|
||||
throws (-> CoffeeScript.compile "{a, r..., s...} = x"), null, "multiple rest elements are disallowed"
|
||||
throws (-> CoffeeScript.compile "{a, r..., s..., b} = x"), null, "multiple rest elements are disallowed"
|
||||
prop = "b"
|
||||
{a, b, r...} = obj
|
||||
eq a, 1
|
||||
eq b, 2
|
||||
eq r.e, obj.e
|
||||
eq r.a, undefined
|
||||
{d, c: x, r...} = obj
|
||||
eq x, 3
|
||||
eq d, 4
|
||||
eq r.c, undefined
|
||||
eq r.b, 2
|
||||
{a, 'b': z, g = 9, r...} = obj
|
||||
eq g, 9
|
||||
eq z, 2
|
||||
eq r.b, undefined
|
||||
|
||||
test "destructuring assignment with splats and default values", ->
|
||||
obj = {}
|
||||
c = {b: 1}
|
||||
{ a: {b} = c, d...} = obj
|
||||
|
||||
eq b, 1
|
||||
deepEqual d, {}
|
||||
|
||||
test "destructuring assignment with splat with default value", ->
|
||||
obj = {}
|
||||
c = {val: 1}
|
||||
{ a: {b...} = c } = obj
|
||||
|
||||
deepEqual b, val: 1
|
||||
|
||||
test "destructuring assignment with multiple splats in different objects", ->
|
||||
obj = { a: {val: 1}, b: {val: 2} }
|
||||
{ a: {a...}, b: {b...} } = obj
|
||||
deepEqual a, val: 1
|
||||
deepEqual b, val: 2
|
||||
|
||||
test "destructuring assignment with dynamic keys and splats", ->
|
||||
i = 0
|
||||
foo = -> ++i
|
||||
|
||||
obj = {1: 'a', 2: 'b'}
|
||||
{ "#{foo()}": a, b... } = obj
|
||||
|
||||
eq a, 'a'
|
||||
eq i, 1
|
||||
deepEqual b, 2: 'b'
|
||||
|
||||
# Tests from https://babeljs.io/docs/plugins/transform-object-rest-spread/.
|
||||
test "destructuring assignment with objects and splats: Babel tests", ->
|
||||
# What Babel calls “rest properties:”
|
||||
{ x, y, z... } = { x: 1, y: 2, a: 3, b: 4 }
|
||||
eq x, 1
|
||||
eq y, 2
|
||||
deepEqual z, { a: 3, b: 4 }
|
||||
|
||||
# What Babel calls “spread properties:”
|
||||
n = { x, y, z... }
|
||||
deepEqual n, { x: 1, y: 2, a: 3, b: 4 }
|
||||
|
||||
test "deep destructuring assignment with objects: ES2015", ->
|
||||
a1={}; b1={}; c1={}; d1={}
|
||||
obj = {
|
||||
a: a1
|
||||
b: {
|
||||
'c': {
|
||||
d: {
|
||||
b1
|
||||
e: c1
|
||||
f: d1
|
||||
}
|
||||
}
|
||||
}
|
||||
b2: {b1, c1}
|
||||
}
|
||||
{a: w, b: {c: {d: {b1: bb, r1...}}}, r2...} = obj
|
||||
eq r1.e, c1
|
||||
eq r2.b, undefined
|
||||
eq bb, b1
|
||||
eq r2.b2, obj.b2
|
||||
|
||||
test "deep destructuring assignment with defaults: ES2015", ->
|
||||
obj =
|
||||
b: { c: 1, baz: 'qux' }
|
||||
foo: 'bar'
|
||||
j =
|
||||
f: 'world'
|
||||
i =
|
||||
some: 'prop'
|
||||
{
|
||||
a...
|
||||
b: { c, d... }
|
||||
e: {
|
||||
f: hello
|
||||
g: { h... } = i
|
||||
} = j
|
||||
} = obj
|
||||
|
||||
deepEqual a, foo: 'bar'
|
||||
eq c, 1
|
||||
deepEqual d, baz: 'qux'
|
||||
eq hello, 'world'
|
||||
deepEqual h, some: 'prop'
|
||||
|
||||
test "object spread properties: ES2015", ->
|
||||
obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
|
||||
obj2 = {obj..., c:9}
|
||||
eq obj2.c, 9
|
||||
eq obj.a, obj2.a
|
||||
|
||||
obj2 = {obj..., a: 8, c: 9, obj...}
|
||||
eq obj2.c, 3
|
||||
eq obj.a, obj2.a
|
||||
|
||||
obj3 = {obj..., b: 7, g: {obj2..., c: 1}}
|
||||
eq obj3.g.c, 1
|
||||
eq obj3.b, 7
|
||||
deepEqual obj3.g, {obj..., c: 1}
|
||||
|
||||
(({a, b, r...}) ->
|
||||
eq 1, a
|
||||
deepEqual r, {c: 3, d: 44, e: 55}
|
||||
) {obj2..., d: 44, e: 55}
|
||||
|
||||
obj = {a: 1, b: 2, c: {d: 3, e: 4, f: {g: 5}}}
|
||||
obj4 = {a: 10, obj.c...}
|
||||
eq obj4.a, 10
|
||||
eq obj4.d, 3
|
||||
eq obj4.f.g, 5
|
||||
deepEqual obj4.f, obj.c.f
|
||||
|
||||
obj5 = {obj..., ((k) -> {b: k})(99)...}
|
||||
eq obj5.b, 99
|
||||
deepEqual obj5.c, obj.c
|
||||
|
||||
fn = -> {c: {d: 33, e: 44, f: {g: 55}}}
|
||||
obj6 = {obj..., fn()...}
|
||||
eq obj6.c.d, 33
|
||||
deepEqual obj6.c, {d: 33, e: 44, f: {g: 55}}
|
||||
|
||||
obj7 = {obj..., fn()..., {c: {d: 55, e: 66, f: {77}}}...}
|
||||
eq obj7.c.d, 55
|
||||
deepEqual obj6.c, {d: 33, e: 44, f: {g: 55}}
|
||||
|
||||
test "bracket insertion when necessary", ->
|
||||
[a] = [0] ? [1]
|
||||
eq a, 0
|
||||
@@ -562,11 +710,22 @@ test "Assignment to variables similar to helper functions", ->
|
||||
extend = 3
|
||||
hasProp = 4
|
||||
value: 5
|
||||
method: (bind, bind1) -> [bind, bind1, extend, hasProp, @value]
|
||||
arrayEq [1, 2, 3, 4, 5], new B().method 1, 2
|
||||
method: (bind, bind1) => [bind, bind1, extend, hasProp, @value]
|
||||
{method} = new B
|
||||
arrayEq [1, 2, 3, 4, 5], method 1, 2
|
||||
|
||||
modulo = -1 %% 3
|
||||
eq 2, modulo
|
||||
|
||||
indexOf = [1, 2, 3]
|
||||
ok 2 in indexOf
|
||||
|
||||
test "#4566: destructuring with nested default values", ->
|
||||
{a: {b = 1}} = a: {}
|
||||
eq 1, b
|
||||
|
||||
{c: {d} = {}} = c: d: 3
|
||||
eq 3, d
|
||||
|
||||
{e: {f = 5} = {}} = {}
|
||||
eq 5, f
|
||||
|
||||
@@ -136,20 +136,42 @@ test "classes with JS-keyword properties", ->
|
||||
ok instance.name() is 'class'
|
||||
|
||||
|
||||
test "Classes with methods that are pre-bound statically, to the class", ->
|
||||
test "Classes with methods that are pre-bound to the instance, or statically, to the class", ->
|
||||
|
||||
class Dog
|
||||
constructor: (name) ->
|
||||
@name = name
|
||||
|
||||
bark: =>
|
||||
"#{@name} woofs!"
|
||||
|
||||
@static = =>
|
||||
new this('Dog')
|
||||
|
||||
spark = new Dog('Spark')
|
||||
fido = new Dog('Fido')
|
||||
fido.bark = spark.bark
|
||||
|
||||
ok fido.bark() is 'Spark woofs!'
|
||||
|
||||
obj = func: Dog.static
|
||||
|
||||
ok obj.func().name is 'Dog'
|
||||
|
||||
|
||||
test "a bound function in a bound function", ->
|
||||
|
||||
class Mini
|
||||
num: 10
|
||||
generate: =>
|
||||
for i in [1..3]
|
||||
=>
|
||||
@num
|
||||
|
||||
m = new Mini
|
||||
eq (func() for func in m.generate()).join(' '), '10 10 10'
|
||||
|
||||
|
||||
test "contructor called with varargs", ->
|
||||
|
||||
class Connection
|
||||
@@ -454,6 +476,21 @@ test "#1182: execution order needs to be considered as well", ->
|
||||
@B: makeFn 2
|
||||
constructor: makeFn 3
|
||||
|
||||
test "#1182: external constructors with bound functions", ->
|
||||
fn = ->
|
||||
{one: 1}
|
||||
this
|
||||
class B
|
||||
class A
|
||||
constructor: fn
|
||||
method: => this instanceof A
|
||||
ok (new A).method.call(new B)
|
||||
|
||||
test "#1372: bound class methods with reserved names", ->
|
||||
class C
|
||||
delete: =>
|
||||
ok C::delete
|
||||
|
||||
test "#1380: `super` with reserved names", ->
|
||||
class C
|
||||
do: -> super()
|
||||
@@ -522,7 +559,7 @@ test "#1842: Regression with bound functions within bound class methods", ->
|
||||
@unbound: ->
|
||||
eq this, Store
|
||||
|
||||
instance: ->
|
||||
instance: =>
|
||||
ok this instanceof Store
|
||||
|
||||
Store.bound()
|
||||
@@ -685,6 +722,57 @@ test "extending native objects works with and without defining a constructor", -
|
||||
ok overrideArray instanceof OverrideArray
|
||||
eq 'yes!', overrideArray.method()
|
||||
|
||||
|
||||
test "#2782: non-alphanumeric-named bound functions", ->
|
||||
class A
|
||||
'b:c': =>
|
||||
'd'
|
||||
|
||||
eq (new A)['b:c'](), 'd'
|
||||
|
||||
|
||||
test "#2781: overriding bound functions", ->
|
||||
class A
|
||||
a: ->
|
||||
@b()
|
||||
b: =>
|
||||
1
|
||||
|
||||
class B extends A
|
||||
b: =>
|
||||
2
|
||||
|
||||
b = (new A).b
|
||||
eq b(), 1
|
||||
|
||||
b = (new B).b
|
||||
eq b(), 2
|
||||
|
||||
|
||||
test "#2791: bound function with destructured argument", ->
|
||||
class Foo
|
||||
method: ({a}) => 'Bar'
|
||||
|
||||
eq (new Foo).method({a: 'Bar'}), 'Bar'
|
||||
|
||||
|
||||
test "#2796: ditto, ditto, ditto", ->
|
||||
answer = null
|
||||
|
||||
outsideMethod = (func) ->
|
||||
func.call message: 'wrong!'
|
||||
|
||||
class Base
|
||||
constructor: ->
|
||||
@message = 'right!'
|
||||
outsideMethod @echo
|
||||
|
||||
echo: =>
|
||||
answer = @message
|
||||
|
||||
new Base
|
||||
eq answer, 'right!'
|
||||
|
||||
test "#3063: Class bodies cannot contain pure statements", ->
|
||||
throws -> CoffeeScript.compile """
|
||||
class extends S
|
||||
@@ -734,6 +822,10 @@ test "#1392 calling `super` in methods defined on namespaced classes", ->
|
||||
eq 5, (new C.a).m()
|
||||
|
||||
|
||||
test "#4436 immediately instantiated named class", ->
|
||||
ok new class Foo
|
||||
|
||||
|
||||
test "dynamic method names", ->
|
||||
class A
|
||||
"#{name = 'm'}": -> 1
|
||||
@@ -896,6 +988,9 @@ test "`this` access after `super` in extended classes", ->
|
||||
eq result.super, this
|
||||
eq result.param, @param
|
||||
eq result.method, @method
|
||||
ok result.method isnt Test::method
|
||||
|
||||
method: =>
|
||||
|
||||
nonce = {}
|
||||
new Test nonce, {}
|
||||
@@ -915,6 +1010,8 @@ test "`@`-params and bound methods with multiple `super` paths (blocks)", ->
|
||||
super 'not param'
|
||||
eq @name, 'not param'
|
||||
eq @param, nonce
|
||||
ok @method isnt Test::method
|
||||
method: =>
|
||||
new Test true, nonce
|
||||
new Test false, nonce
|
||||
|
||||
@@ -933,13 +1030,16 @@ test "`@`-params and bound methods with multiple `super` paths (expressions)", -
|
||||
eq (super 'param'), @;
|
||||
eq @name, 'param';
|
||||
eq @param, nonce;
|
||||
ok @method isnt Test::method
|
||||
)
|
||||
else
|
||||
result = (
|
||||
eq (super 'not param'), @;
|
||||
eq @name, 'not param';
|
||||
eq @param, nonce;
|
||||
ok @method isnt Test::method
|
||||
)
|
||||
method: =>
|
||||
new Test true, nonce
|
||||
new Test false, nonce
|
||||
|
||||
@@ -1141,7 +1241,7 @@ test "super in a bound function", ->
|
||||
make: -> "Making a #{@drink}"
|
||||
|
||||
class B extends A
|
||||
make: (@flavor) ->
|
||||
make: (@flavor) =>
|
||||
super() + " with #{@flavor}"
|
||||
|
||||
b = new B('Machiato')
|
||||
@@ -1149,7 +1249,7 @@ test "super in a bound function", ->
|
||||
|
||||
# super in a bound function in a bound function
|
||||
class C extends A
|
||||
make: (@flavor) ->
|
||||
make: (@flavor) =>
|
||||
func = () =>
|
||||
super() + " with #{@flavor}"
|
||||
func()
|
||||
@@ -1574,3 +1674,137 @@ test "CS6 Class extends a CS1 compiled class with super()", ->
|
||||
eq B.className(), 'ExtendedCS1'
|
||||
b = new B('three')
|
||||
eq b.make(), "making a cafe ole with caramel and three shots of espresso"
|
||||
|
||||
test 'Bound method called normally before binding is ok', ->
|
||||
class Base
|
||||
constructor: ->
|
||||
@setProp()
|
||||
eq @derivedBound(), 3
|
||||
|
||||
class Derived extends Base
|
||||
setProp: ->
|
||||
@prop = 3
|
||||
|
||||
derivedBound: =>
|
||||
@prop
|
||||
|
||||
d = new Derived
|
||||
|
||||
test 'Bound method called as callback after super() is ok', ->
|
||||
class Base
|
||||
|
||||
class Derived extends Base
|
||||
constructor: (@prop = 3) ->
|
||||
super()
|
||||
f = @derivedBound
|
||||
eq f(), 3
|
||||
|
||||
derivedBound: =>
|
||||
@prop
|
||||
|
||||
d = new Derived
|
||||
{derivedBound} = d
|
||||
eq derivedBound(), 3
|
||||
|
||||
test 'Bound method of base class called as callback is ok', ->
|
||||
class Base
|
||||
constructor: (@prop = 3) ->
|
||||
f = @baseBound
|
||||
eq f(), 3
|
||||
|
||||
baseBound: =>
|
||||
@prop
|
||||
|
||||
b = new Base
|
||||
{baseBound} = b
|
||||
eq baseBound(), 3
|
||||
|
||||
test 'Bound method of prop-named class called as callback is ok', ->
|
||||
Hive = {}
|
||||
class Hive.Bee
|
||||
constructor: (@prop = 3) ->
|
||||
f = @baseBound
|
||||
eq f(), 3
|
||||
|
||||
baseBound: =>
|
||||
@prop
|
||||
|
||||
b = new Hive.Bee
|
||||
{baseBound} = b
|
||||
eq baseBound(), 3
|
||||
|
||||
test 'Bound method of class with expression base class called as callback is ok', ->
|
||||
calledB = no
|
||||
B = ->
|
||||
throw new Error if calledB
|
||||
calledB = yes
|
||||
class
|
||||
class A extends B()
|
||||
constructor: (@prop = 3) ->
|
||||
super()
|
||||
f = @derivedBound
|
||||
eq f(), 3
|
||||
|
||||
derivedBound: =>
|
||||
@prop
|
||||
|
||||
b = new A
|
||||
{derivedBound} = b
|
||||
eq derivedBound(), 3
|
||||
|
||||
test 'Bound method of class with expression class name called as callback is ok', ->
|
||||
calledF = no
|
||||
obj = {}
|
||||
B = class
|
||||
f = ->
|
||||
throw new Error if calledF
|
||||
calledF = yes
|
||||
obj
|
||||
class f().A extends B
|
||||
constructor: (@prop = 3) ->
|
||||
super()
|
||||
g = @derivedBound
|
||||
eq g(), 3
|
||||
|
||||
derivedBound: =>
|
||||
@prop
|
||||
|
||||
a = new obj.A
|
||||
{derivedBound} = a
|
||||
eq derivedBound(), 3
|
||||
|
||||
test 'Bound method of anonymous child class called as callback is ok', ->
|
||||
f = ->
|
||||
B = class
|
||||
class extends B
|
||||
constructor: (@prop = 3) ->
|
||||
super()
|
||||
g = @derivedBound
|
||||
eq g(), 3
|
||||
|
||||
derivedBound: =>
|
||||
@prop
|
||||
|
||||
a = new (f())
|
||||
{derivedBound} = a
|
||||
eq derivedBound(), 3
|
||||
|
||||
test 'Bound method of immediately instantiated class with expression base class called as callback is ok', ->
|
||||
calledF = no
|
||||
obj = {}
|
||||
B = class
|
||||
f = ->
|
||||
throw new Error if calledF
|
||||
calledF = yes
|
||||
obj
|
||||
a = new class f().A extends B
|
||||
constructor: (@prop = 3) ->
|
||||
super()
|
||||
g = @derivedBound
|
||||
eq g(), 3
|
||||
|
||||
derivedBound: =>
|
||||
@prop
|
||||
|
||||
{derivedBound} = a
|
||||
eq derivedBound(), 3
|
||||
|
||||
@@ -213,57 +213,50 @@ test "#2916: block comment before implicit call with implicit object", ->
|
||||
a: yes
|
||||
|
||||
test "#3132: Format single-line block comment nicely", ->
|
||||
input = """
|
||||
### Single-line block comment without additional space here => ###"""
|
||||
|
||||
output = """
|
||||
eqJS """
|
||||
### Single-line block comment without additional space here => ###""",
|
||||
"""
|
||||
/* Single-line block comment without additional space here => */
|
||||
"""
|
||||
eq toJS(input), output
|
||||
|
||||
test "#3132: Format multi-line block comment nicely", ->
|
||||
input = """
|
||||
eqJS """
|
||||
###
|
||||
# Multi-line
|
||||
# block
|
||||
# comment
|
||||
###"""
|
||||
|
||||
output = """
|
||||
###""",
|
||||
"""
|
||||
/*
|
||||
* Multi-line
|
||||
* block
|
||||
* comment
|
||||
*/
|
||||
"""
|
||||
eq toJS(input), output
|
||||
|
||||
test "#3132: Format simple block comment nicely", ->
|
||||
input = """
|
||||
eqJS """
|
||||
###
|
||||
No
|
||||
Preceding hash
|
||||
###"""
|
||||
|
||||
output = """
|
||||
###""",
|
||||
"""
|
||||
/*
|
||||
No
|
||||
Preceding hash
|
||||
*/
|
||||
"""
|
||||
|
||||
eq toJS(input), output
|
||||
|
||||
test "#3132: Format indented block-comment nicely", ->
|
||||
input = """
|
||||
eqJS """
|
||||
fn = () ->
|
||||
###
|
||||
# Indented
|
||||
Multiline
|
||||
###
|
||||
1"""
|
||||
|
||||
output = """
|
||||
1""",
|
||||
"""
|
||||
var fn;
|
||||
|
||||
fn = function() {
|
||||
@@ -275,21 +268,19 @@ test "#3132: Format indented block-comment nicely", ->
|
||||
return 1;
|
||||
};
|
||||
"""
|
||||
eq toJS(input), output
|
||||
|
||||
# Although adequately working, block comment-placement is not yet perfect.
|
||||
# (Considering a case where multiple variables have been declared …)
|
||||
test "#3132: Format jsdoc-style block-comment nicely", ->
|
||||
input = """
|
||||
eqJS """
|
||||
###*
|
||||
# Multiline for jsdoc-"@doctags"
|
||||
#
|
||||
# @type {Function}
|
||||
###
|
||||
fn = () -> 1
|
||||
""",
|
||||
"""
|
||||
|
||||
output = """
|
||||
/**
|
||||
* Multiline for jsdoc-"@doctags"
|
||||
*
|
||||
@@ -300,21 +291,19 @@ test "#3132: Format jsdoc-style block-comment nicely", ->
|
||||
fn = function() {
|
||||
return 1;
|
||||
};"""
|
||||
eq toJS(input), output
|
||||
|
||||
# Although adequately working, block comment-placement is not yet perfect.
|
||||
# (Considering a case where multiple variables have been declared …)
|
||||
test "#3132: Format hand-made (raw) jsdoc-style block-comment nicely", ->
|
||||
input = """
|
||||
eqJS """
|
||||
###*
|
||||
* Multiline for jsdoc-"@doctags"
|
||||
*
|
||||
* @type {Function}
|
||||
###
|
||||
fn = () -> 1
|
||||
""",
|
||||
"""
|
||||
|
||||
output = """
|
||||
/**
|
||||
* Multiline for jsdoc-"@doctags"
|
||||
*
|
||||
@@ -325,12 +314,11 @@ test "#3132: Format hand-made (raw) jsdoc-style block-comment nicely", ->
|
||||
fn = function() {
|
||||
return 1;
|
||||
};"""
|
||||
eq toJS(input), output
|
||||
|
||||
# Although adequately working, block comment-placement is not yet perfect.
|
||||
# (Considering a case where multiple variables have been declared …)
|
||||
test "#3132: Place block-comments nicely", ->
|
||||
input = """
|
||||
eqJS """
|
||||
###*
|
||||
# A dummy class definition
|
||||
#
|
||||
@@ -350,9 +338,8 @@ test "#3132: Place block-comments nicely", ->
|
||||
###
|
||||
@instance = new DummyClass()
|
||||
|
||||
""",
|
||||
"""
|
||||
|
||||
output = """
|
||||
/**
|
||||
* A dummy class definition
|
||||
*
|
||||
@@ -383,22 +370,19 @@ test "#3132: Place block-comments nicely", ->
|
||||
return DummyClass;
|
||||
|
||||
})();"""
|
||||
eq toJS(input), output
|
||||
|
||||
test "#3638: Demand a whitespace after # symbol", ->
|
||||
input = """
|
||||
eqJS """
|
||||
###
|
||||
#No
|
||||
#whitespace
|
||||
###"""
|
||||
|
||||
output = """
|
||||
###""",
|
||||
"""
|
||||
/*
|
||||
#No
|
||||
#whitespace
|
||||
*/"""
|
||||
|
||||
eq toJS(input), output
|
||||
|
||||
test "#3761: Multiline comment at end of an object", ->
|
||||
anObject =
|
||||
|
||||
726
test/csx.coffee
Normal file
726
test/csx.coffee
Normal file
@@ -0,0 +1,726 @@
|
||||
# We usually do not check the actual JS output from the compiler, but since
|
||||
# JSX is not natively supported by Node, we do it in this case.
|
||||
|
||||
test 'self closing', ->
|
||||
eqJS '''
|
||||
<div />
|
||||
''', '''
|
||||
<div />;
|
||||
'''
|
||||
|
||||
test 'self closing formatting', ->
|
||||
eqJS '''
|
||||
<div/>
|
||||
''', '''
|
||||
<div />;
|
||||
'''
|
||||
|
||||
test 'self closing multiline', ->
|
||||
eqJS '''
|
||||
<div
|
||||
/>
|
||||
''', '''
|
||||
<div />;
|
||||
'''
|
||||
|
||||
test 'regex attribute', ->
|
||||
eqJS '''
|
||||
<div x={/>asds/} />
|
||||
''', '''
|
||||
<div x={/>asds/} />;
|
||||
'''
|
||||
|
||||
test 'string attribute', ->
|
||||
eqJS '''
|
||||
<div x="a" />
|
||||
''', '''
|
||||
<div x="a" />;
|
||||
'''
|
||||
|
||||
test 'simple attribute', ->
|
||||
eqJS '''
|
||||
<div x={42} />
|
||||
''', '''
|
||||
<div x={42} />;
|
||||
'''
|
||||
|
||||
test 'assignment attribute', ->
|
||||
eqJS '''
|
||||
<div x={y = 42} />
|
||||
''', '''
|
||||
var y;
|
||||
|
||||
<div x={y = 42} />;
|
||||
'''
|
||||
|
||||
test 'object attribute', ->
|
||||
eqJS '''
|
||||
<div x={{y: 42}} />
|
||||
''', '''
|
||||
<div x={{
|
||||
y: 42
|
||||
}} />;
|
||||
'''
|
||||
|
||||
test 'attribute without value', ->
|
||||
eqJS '''
|
||||
<div checked x="hello" />
|
||||
''', '''
|
||||
<div checked x="hello" />;
|
||||
'''
|
||||
|
||||
test 'paired', ->
|
||||
eqJS '''
|
||||
<div></div>
|
||||
''', '''
|
||||
<div></div>;
|
||||
'''
|
||||
|
||||
test 'simple content', ->
|
||||
eqJS '''
|
||||
<div>Hello world</div>
|
||||
''', '''
|
||||
<div>Hello world</div>;
|
||||
'''
|
||||
|
||||
test 'content interpolation', ->
|
||||
eqJS '''
|
||||
<div>Hello {42}</div>
|
||||
''', '''
|
||||
<div>Hello {42}</div>;
|
||||
'''
|
||||
|
||||
test 'nested tag', ->
|
||||
eqJS '''
|
||||
<div><span /></div>
|
||||
''', '''
|
||||
<div><span /></div>;
|
||||
'''
|
||||
|
||||
test 'tag inside interpolation formatting', ->
|
||||
eqJS '''
|
||||
<div>Hello {<span />}</div>
|
||||
''', '''
|
||||
<div>Hello <span /></div>;
|
||||
'''
|
||||
|
||||
test 'tag inside interpolation, tags are callable', ->
|
||||
eqJS '''
|
||||
<div>Hello {<span /> x}</div>
|
||||
''', '''
|
||||
<div>Hello {<span />(x)}</div>;
|
||||
'''
|
||||
|
||||
test 'tags inside interpolation, tags trigger implicit calls', ->
|
||||
eqJS '''
|
||||
<div>Hello {f <span />}</div>
|
||||
''', '''
|
||||
<div>Hello {f(<span />)}</div>;
|
||||
'''
|
||||
|
||||
test 'regex in interpolation', ->
|
||||
eqJS '''
|
||||
<div x={/>asds/}><div />{/>asdsad</}</div>
|
||||
''', '''
|
||||
<div x={/>asds/}><div />{/>asdsad</}</div>;
|
||||
'''
|
||||
|
||||
test 'interpolation in string attribute value', ->
|
||||
eqJS '''
|
||||
<div x="Hello #{world}" />
|
||||
''', '''
|
||||
<div x={`Hello ${world}`} />;
|
||||
'''
|
||||
|
||||
# Unlike in `coffee-react-transform`.
|
||||
test 'bare numbers not allowed', ->
|
||||
throws -> CoffeeScript.compile '<div x=3 />'
|
||||
|
||||
test 'bare expressions not allowed', ->
|
||||
throws -> CoffeeScript.compile '<div x=y />'
|
||||
|
||||
test 'bare complex expressions not allowed', ->
|
||||
throws -> CoffeeScript.compile '<div x=f(3) />'
|
||||
|
||||
test 'unescaped opening tag angle bracket disallowed', ->
|
||||
throws -> CoffeeScript.compile '<Person><<</Person>'
|
||||
|
||||
test 'space around equal sign', ->
|
||||
eqJS '''
|
||||
<div popular = "yes" />
|
||||
''', '''
|
||||
<div popular="yes" />;
|
||||
'''
|
||||
|
||||
# The following tests were adopted from James Friend’s
|
||||
# [https://github.com/jsdf/coffee-react-transform](https://github.com/jsdf/coffee-react-transform).
|
||||
|
||||
test 'ambiguous tag-like expression', ->
|
||||
throws -> CoffeeScript.compile 'x = a <b > c'
|
||||
|
||||
test 'ambiguous tag', ->
|
||||
eqJS '''
|
||||
a <b > c </b>
|
||||
''', '''
|
||||
a(<b> c </b>);
|
||||
'''
|
||||
|
||||
test 'escaped CoffeeScript attribute', ->
|
||||
eqJS '''
|
||||
<Person name={if test() then 'yes' else 'no'} />
|
||||
''', '''
|
||||
<Person name={test() ? 'yes' : 'no'} />;
|
||||
'''
|
||||
|
||||
test 'escaped CoffeeScript attribute over multiple lines', ->
|
||||
eqJS '''
|
||||
<Person name={
|
||||
if test()
|
||||
'yes'
|
||||
else
|
||||
'no'
|
||||
} />
|
||||
''', '''
|
||||
<Person name={test() ? 'yes' : 'no'} />;
|
||||
'''
|
||||
|
||||
test 'multiple line escaped CoffeeScript with nested CSX', ->
|
||||
eqJS '''
|
||||
<Person name={
|
||||
if test()
|
||||
'yes'
|
||||
else
|
||||
'no'
|
||||
}>
|
||||
{
|
||||
|
||||
for n in a
|
||||
<div> a
|
||||
asf
|
||||
<li xy={"as"}>{ n+1 }<a /> <a /> </li>
|
||||
</div>
|
||||
}
|
||||
|
||||
</Person>
|
||||
''', '''
|
||||
var n;
|
||||
|
||||
<Person name={test() ? 'yes' : 'no'}>
|
||||
{(function() {
|
||||
var i, len, results;
|
||||
results = [];
|
||||
for (i = 0, len = a.length; i < len; i++) {
|
||||
n = a[i];
|
||||
results.push(<div> a
|
||||
asf
|
||||
<li xy={"as"}>{n + 1}<a /> <a /> </li>
|
||||
</div>);
|
||||
}
|
||||
return results;
|
||||
})()}
|
||||
|
||||
</Person>;
|
||||
'''
|
||||
|
||||
test 'nested CSX within an attribute, with object attr value', ->
|
||||
eqJS '''
|
||||
<Company>
|
||||
<Person name={<NameComponent attr3={ {'a': {}, b: '{'} } />} />
|
||||
</Company>
|
||||
''', '''
|
||||
<Company>
|
||||
<Person name={<NameComponent attr3={{
|
||||
'a': {},
|
||||
b: '{'
|
||||
}} />} />
|
||||
</Company>;
|
||||
'''
|
||||
|
||||
test 'complex nesting', ->
|
||||
eqJS '''
|
||||
<div code={someFunc({a:{b:{}, C:'}{}{'}})} />
|
||||
''', '''
|
||||
<div code={someFunc({
|
||||
a: {
|
||||
b: {},
|
||||
C: '}{}{'
|
||||
}
|
||||
})} />;
|
||||
'''
|
||||
|
||||
test 'multiline tag with nested CSX within an attribute', ->
|
||||
eqJS '''
|
||||
<Person
|
||||
name={
|
||||
name = formatName(user.name)
|
||||
<NameComponent name={name.toUppercase()} />
|
||||
}
|
||||
>
|
||||
blah blah blah
|
||||
</Person>
|
||||
''', '''
|
||||
var name;
|
||||
|
||||
<Person name={name = formatName(user.name), <NameComponent name={name.toUppercase()} />}>
|
||||
blah blah blah
|
||||
</Person>;
|
||||
'''
|
||||
|
||||
test 'escaped CoffeeScript with nested object literals', ->
|
||||
eqJS '''
|
||||
<Person>
|
||||
blah blah blah {
|
||||
{'a' : {}, 'asd': 'asd'}
|
||||
}
|
||||
</Person>
|
||||
''', '''
|
||||
<Person>
|
||||
blah blah blah {{
|
||||
'a': {},
|
||||
'asd': 'asd'
|
||||
}}
|
||||
</Person>;
|
||||
'''
|
||||
|
||||
test 'multiline tag attributes with escaped CoffeeScript', ->
|
||||
eqJS '''
|
||||
<Person name={if isActive() then 'active' else 'inactive'}
|
||||
someattr='on new line' />
|
||||
''', '''
|
||||
<Person name={isActive() ? 'active' : 'inactive'} someattr='on new line' />;
|
||||
'''
|
||||
|
||||
test 'lots of attributes', ->
|
||||
eqJS '''
|
||||
<Person eyes={2} friends={getFriends()} popular = "yes"
|
||||
active={ if isActive() then 'active' else 'inactive' } data-attr='works' checked check={me_out}
|
||||
/>
|
||||
''', '''
|
||||
<Person eyes={2} friends={getFriends()} popular="yes" active={isActive() ? 'active' : 'inactive'} data-attr='works' checked check={me_out} />;
|
||||
'''
|
||||
|
||||
# TODO: fix partially indented CSX
|
||||
# test 'multiline elements', ->
|
||||
# eqJS '''
|
||||
# <div something={
|
||||
# do ->
|
||||
# test = /432/gm # this is a regex
|
||||
# 6 /432/gm # this is division
|
||||
# }
|
||||
# >
|
||||
# <div>
|
||||
# <div>
|
||||
# <div>
|
||||
# <article name={ new Date() } number={203}
|
||||
# range={getRange()}
|
||||
# >
|
||||
# </article>
|
||||
# </div>
|
||||
# </div>
|
||||
# </div>
|
||||
# </div>
|
||||
# ''', '''
|
||||
# bla
|
||||
# '''
|
||||
|
||||
test 'complex regex', ->
|
||||
eqJS '''
|
||||
<Person />
|
||||
/\\/\\/<Person \\/>\\>\\//
|
||||
''', '''
|
||||
<Person />;
|
||||
|
||||
/\\/\\/<Person \\/>\\>\\//;
|
||||
'''
|
||||
|
||||
test 'heregex', ->
|
||||
eqJS '''
|
||||
test = /432/gm # this is a regex
|
||||
6 /432/gm # this is division
|
||||
<Tag>
|
||||
{test = /<Tag>/} this is a regex containing something which looks like a tag
|
||||
</Tag>
|
||||
<Person />
|
||||
REGEX = /// ^
|
||||
(/ (?! [\s=] ) # comment comment <comment>comment</comment>
|
||||
[^ [ / \n \\ ]* # comment comment
|
||||
(?:
|
||||
<Tag />
|
||||
(?: \\[\s\S] # comment comment
|
||||
| \[ # comment comment
|
||||
[^ \] \n \\ ]*
|
||||
(?: \\[\s\S] [^ \] \n \\ ]* )*
|
||||
<Tag>tag</Tag>
|
||||
]
|
||||
) [^ [ / \n \\ ]*
|
||||
)*
|
||||
/) ([imgy]{0,4}) (?!\w)
|
||||
///
|
||||
<Person />
|
||||
''', '''
|
||||
var REGEX, test;
|
||||
|
||||
test = /432/gm;
|
||||
|
||||
6 / 432 / gm;
|
||||
|
||||
<Tag>
|
||||
{(test = /<Tag>/)} this is a regex containing something which looks like a tag
|
||||
</Tag>;
|
||||
|
||||
<Person />;
|
||||
|
||||
REGEX = /^(\\/(?![s=])[^[\\/ ]*(?:<Tag\\/>(?:\\[sS]|[[^] ]*(?:\\[sS][^] ]*)*<Tag>tag<\\/Tag>])[^[\\/ ]*)*\\/)([imgy]{0,4})(?!w)/;
|
||||
|
||||
<Person />;
|
||||
'''
|
||||
|
||||
test 'comment within CSX is not treated as comment', ->
|
||||
eqJS '''
|
||||
<Person>
|
||||
# i am not a comment
|
||||
</Person>
|
||||
''', '''
|
||||
<Person>
|
||||
# i am not a comment
|
||||
</Person>;
|
||||
'''
|
||||
|
||||
test 'comment at start of CSX escape', ->
|
||||
eqJS '''
|
||||
<Person>
|
||||
{# i am a comment
|
||||
"i am a string"
|
||||
}
|
||||
</Person>
|
||||
''', '''
|
||||
<Person>
|
||||
{"i am a string"}
|
||||
</Person>;
|
||||
'''
|
||||
|
||||
test 'CSX comment cannot be used inside interpolation', ->
|
||||
throws -> CoffeeScript.compile '''
|
||||
<Person>
|
||||
{# i am a comment}
|
||||
</Person>
|
||||
'''
|
||||
|
||||
test 'comment syntax cannot be used inline', ->
|
||||
throws -> CoffeeScript.compile '''
|
||||
<Person>{#comment inline}</Person>
|
||||
'''
|
||||
|
||||
test 'string within CSX is ignored', ->
|
||||
eqJS '''
|
||||
<Person> "i am not a string" 'nor am i' </Person>
|
||||
''', '''
|
||||
<Person> "i am not a string" 'nor am i' </Person>;
|
||||
'''
|
||||
|
||||
test 'special chars within CSX are ignored', ->
|
||||
eqJS """
|
||||
<Person> a,/';][' a\''@$%^&˚¬∑˜˚∆å∂¬˚*()*&^%$>> '"''"'''\'\'m' i </Person>
|
||||
""", """
|
||||
<Person> a,/';][' a''@$%^&˚¬∑˜˚∆å∂¬˚*()*&^%$>> '"''"'''''m' i </Person>;
|
||||
"""
|
||||
|
||||
test 'html entities (name, decimal, hex) within CSX', ->
|
||||
eqJS '''
|
||||
<Person> &&&€ € €;; </Person>
|
||||
''', '''
|
||||
<Person> &&&€ € €;; </Person>;
|
||||
'''
|
||||
|
||||
test 'tag with {{}}', ->
|
||||
eqJS '''
|
||||
<Person name={{value: item, key, item}} />
|
||||
''', '''
|
||||
<Person name={{
|
||||
value: item,
|
||||
key,
|
||||
item
|
||||
}} />;
|
||||
'''
|
||||
|
||||
test 'tag with namespace', ->
|
||||
eqJS '''
|
||||
<Something.Tag></Something.Tag>
|
||||
''', '''
|
||||
<Something.Tag></Something.Tag>;
|
||||
'''
|
||||
|
||||
test 'tag with lowercase namespace', ->
|
||||
eqJS '''
|
||||
<something.tag></something.tag>
|
||||
''', '''
|
||||
<something.tag></something.tag>;
|
||||
'''
|
||||
|
||||
test 'self closing tag with namespace', ->
|
||||
eqJS '''
|
||||
<Something.Tag />
|
||||
''', '''
|
||||
<Something.Tag />;
|
||||
'''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'self closing tag with spread attribute', ->
|
||||
# eqJS '''
|
||||
# <Component a={b} {... x } b="c" />
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({"a": (b)}, x , {"b": "c"}))
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'complex spread attribute', ->
|
||||
# eqJS '''
|
||||
# <Component {...x} a={b} {... x } b="c" {...$my_xtraCoolVar123 } />
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({}, x, {"a": (b)}, x , {"b": "c"}, $my_xtraCoolVar123 ))
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'multiline spread attribute', ->
|
||||
# eqJS '''
|
||||
# <Component {...
|
||||
# x } a={b} {... x } b="c" {...z }>
|
||||
# </Component>
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({},
|
||||
# x , {"a": (b)}, x , {"b": "c"}, z )
|
||||
# )
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'multiline tag with spread attribute', ->
|
||||
# eqJS '''
|
||||
# <Component
|
||||
# z="1"
|
||||
# {...x}
|
||||
# a={b}
|
||||
# b="c"
|
||||
# >
|
||||
# </Component>
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({ \
|
||||
# "z": "1"
|
||||
# }, x, { \
|
||||
# "a": (b), \
|
||||
# "b": "c"
|
||||
# })
|
||||
# )
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'multiline tag with spread attribute first', ->
|
||||
# eqJS '''
|
||||
# <Component
|
||||
# {...
|
||||
# x}
|
||||
# z="1"
|
||||
# a={b}
|
||||
# b="c"
|
||||
# >
|
||||
# </Component>
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({}, \
|
||||
|
||||
# x, { \
|
||||
# "z": "1", \
|
||||
# "a": (b), \
|
||||
# "b": "c"
|
||||
# })
|
||||
# )
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'complex multiline spread attribute', ->
|
||||
# eqJS '''
|
||||
# <Component
|
||||
# {...
|
||||
# y} a={b} {... x } b="c" {...z }>
|
||||
# <div code={someFunc({a:{b:{}, C:'}'}})} />
|
||||
# </Component>
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({}, \
|
||||
|
||||
# y, {"a": (b)}, x , {"b": "c"}, z ),
|
||||
# React.createElement("div", {"code": (someFunc({a:{b:{}, C:'}'}}))})
|
||||
# )
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'self closing spread attribute on single line', ->
|
||||
# eqJS '''
|
||||
# <Component a="b" c="d" {...@props} />
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({"a": "b", "c": "d"}, @props ))
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'self closing spread attribute on new line', ->
|
||||
# eqJS '''
|
||||
# <Component
|
||||
# a="b"
|
||||
# c="d"
|
||||
# {...@props}
|
||||
# />
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({ \
|
||||
# "a": "b", \
|
||||
# "c": "d"
|
||||
# }, @props
|
||||
# ))
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'self closing spread attribute on same line', ->
|
||||
# eqJS '''
|
||||
# <Component
|
||||
# a="b"
|
||||
# c="d"
|
||||
# {...@props} />
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({ \
|
||||
# "a": "b", \
|
||||
# "c": "d"
|
||||
# }, @props ))
|
||||
# '''
|
||||
|
||||
# TODO: Uncomment the following test once destructured object spreads are supported.
|
||||
# test 'self closing spread attribute on next line', ->
|
||||
# eqJS '''
|
||||
# <Component
|
||||
# a="b"
|
||||
# c="d"
|
||||
# {...@props}
|
||||
|
||||
# />
|
||||
# ''', '''
|
||||
# React.createElement(Component, Object.assign({ \
|
||||
# "a": "b", \
|
||||
# "c": "d"
|
||||
# }, @props
|
||||
|
||||
# ))
|
||||
# '''
|
||||
|
||||
test 'empty strings are not converted to true', ->
|
||||
eqJS '''
|
||||
<Component val="" />
|
||||
''', '''
|
||||
<Component val="" />;
|
||||
'''
|
||||
|
||||
test 'CoffeeScript @ syntax in tag name', ->
|
||||
throws -> CoffeeScript.compile '''
|
||||
<@Component>
|
||||
<Component />
|
||||
</@Component>
|
||||
'''
|
||||
|
||||
test 'hyphens in tag names', ->
|
||||
eqJS '''
|
||||
<paper-button className="button">{text}</paper-button>
|
||||
''', '''
|
||||
<paper-button className="button">{text}</paper-button>;
|
||||
'''
|
||||
|
||||
test 'closing tags must be closed', ->
|
||||
throws -> CoffeeScript.compile '''
|
||||
<a></a
|
||||
'''
|
||||
|
||||
# Tests for allowing less than operator without spaces when ther is no CSX
|
||||
|
||||
test 'unspaced less than without CSX: identifier', ->
|
||||
a = 3
|
||||
div = 5
|
||||
ok a<div
|
||||
|
||||
test 'unspaced less than without CSX: number', ->
|
||||
div = 5
|
||||
ok 3<div
|
||||
|
||||
test 'unspaced less than without CSX: paren', ->
|
||||
div = 5
|
||||
ok (3)<div
|
||||
|
||||
test 'unspaced less than without CSX: index', ->
|
||||
div = 5
|
||||
a = [3]
|
||||
ok a[0]<div
|
||||
|
||||
test 'tag inside CSX works following: identifier', ->
|
||||
eqJS '''
|
||||
<span>a<div /></span>
|
||||
''', '''
|
||||
<span>a<div /></span>;
|
||||
'''
|
||||
|
||||
test 'tag inside CSX works following: number', ->
|
||||
eqJS '''
|
||||
<span>3<div /></span>
|
||||
''', '''
|
||||
<span>3<div /></span>;
|
||||
'''
|
||||
|
||||
test 'tag inside CSX works following: paren', ->
|
||||
eqJS '''
|
||||
<span>(3)<div /></span>
|
||||
''', '''
|
||||
<span>(3)<div /></span>;
|
||||
'''
|
||||
|
||||
test 'tag inside CSX works following: square bracket', ->
|
||||
eqJS '''
|
||||
<span>]<div /></span>
|
||||
''', '''
|
||||
<span>]<div /></span>;
|
||||
'''
|
||||
|
||||
test 'unspaced less than inside CSX works but is not encouraged', ->
|
||||
eqJS '''
|
||||
a = 3
|
||||
div = 5
|
||||
html = <span>{a<div}</span>
|
||||
''', '''
|
||||
var a, div, html;
|
||||
|
||||
a = 3;
|
||||
|
||||
div = 5;
|
||||
|
||||
html = <span>{a < div}</span>;
|
||||
'''
|
||||
|
||||
test 'unspaced less than before CSX works but is not encouraged', ->
|
||||
eqJS '''
|
||||
div = 5
|
||||
res = 2<div
|
||||
html = <span />
|
||||
''', '''
|
||||
var div, html, res;
|
||||
|
||||
div = 5;
|
||||
|
||||
res = 2 < div;
|
||||
|
||||
html = <span />;
|
||||
'''
|
||||
|
||||
test 'unspaced less than after CSX works but is not encouraged', ->
|
||||
eqJS '''
|
||||
div = 5
|
||||
html = <span />
|
||||
res = 2<div
|
||||
''', '''
|
||||
var div, html, res;
|
||||
|
||||
div = 5;
|
||||
|
||||
html = <span />;
|
||||
|
||||
res = 2 < div;
|
||||
'''
|
||||
@@ -804,26 +804,27 @@ test "unexpected object keys", ->
|
||||
[[]]: 1
|
||||
^
|
||||
'''
|
||||
|
||||
assertErrorFormat '''
|
||||
{(a + "b")}
|
||||
''', '''
|
||||
[stdin]:1:2: error: unexpected (
|
||||
[stdin]:1:11: error: unexpected }
|
||||
{(a + "b")}
|
||||
^
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
{(a + "b"): 1}
|
||||
''', '''
|
||||
[stdin]:1:2: error: unexpected (
|
||||
[stdin]:1:11: error: unexpected :
|
||||
{(a + "b"): 1}
|
||||
^
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
(a + "b"): 1
|
||||
''', '''
|
||||
[stdin]:1:1: error: unexpected (
|
||||
[stdin]:1:10: error: unexpected :
|
||||
(a + "b"): 1
|
||||
^
|
||||
^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
a: 1, [[]]: 2
|
||||
@@ -1545,3 +1546,84 @@ test "#4248: Unicode code point escapes", ->
|
||||
'\\u{a}\\u{1111110000}'
|
||||
\ ^\^^^^^^^^^^^^^
|
||||
'''
|
||||
|
||||
test "CSX error: non-matching tag names", ->
|
||||
assertErrorFormat '''
|
||||
<div><span></div></span>
|
||||
''',
|
||||
'''
|
||||
[stdin]:1:7: error: expected corresponding CSX closing tag for span
|
||||
<div><span></div></span>
|
||||
^^^^
|
||||
'''
|
||||
|
||||
test "CSX error: bare expressions not allowed", ->
|
||||
assertErrorFormat '''
|
||||
<div x=3 />
|
||||
''',
|
||||
'''
|
||||
[stdin]:1:8: error: expected wrapped or quoted CSX attribute
|
||||
<div x=3 />
|
||||
^
|
||||
'''
|
||||
|
||||
test "CSX error: unescaped opening tag angle bracket disallowed", ->
|
||||
assertErrorFormat '''
|
||||
<Person><<</Person>
|
||||
''',
|
||||
'''
|
||||
[stdin]:1:9: error: unexpected <<
|
||||
<Person><<</Person>
|
||||
^^
|
||||
'''
|
||||
|
||||
test "CSX error: ambiguous tag-like expression", ->
|
||||
assertErrorFormat '''
|
||||
x = a <b > c
|
||||
''',
|
||||
'''
|
||||
[stdin]:1:10: error: missing </
|
||||
x = a <b > c
|
||||
^
|
||||
'''
|
||||
|
||||
test 'Bound method called as callback before binding throws runtime error', ->
|
||||
class Base
|
||||
constructor: ->
|
||||
f = @derivedBound
|
||||
try
|
||||
f()
|
||||
ok no
|
||||
catch e
|
||||
eq e.message, 'Bound instance method accessed before binding'
|
||||
|
||||
class Derived extends Base
|
||||
derivedBound: =>
|
||||
ok no
|
||||
d = new Derived
|
||||
|
||||
test "#3845/#3446: chain after function glyph (but not inline)", ->
|
||||
assertErrorFormat '''
|
||||
a -> .b
|
||||
''',
|
||||
'''
|
||||
[stdin]:1:6: error: unexpected .
|
||||
a -> .b
|
||||
^
|
||||
'''
|
||||
|
||||
test "#3906: error for unusual indentation", ->
|
||||
assertErrorFormat '''
|
||||
a
|
||||
c
|
||||
.d
|
||||
|
||||
e(
|
||||
f)
|
||||
|
||||
g
|
||||
''', '''
|
||||
[stdin]:2:1: error: unexpected indentation
|
||||
c
|
||||
^^
|
||||
'''
|
||||
|
||||
@@ -338,3 +338,49 @@ test "#4487: Handle unusual outdentation", ->
|
||||
2
|
||||
3
|
||||
eq b, undefined
|
||||
|
||||
test "#3906: handle further indentation inside indented chain", ->
|
||||
eq 1, CoffeeScript.eval '''
|
||||
z = b: -> d: 2
|
||||
e = ->
|
||||
f = 3
|
||||
|
||||
z
|
||||
.b ->
|
||||
c
|
||||
.d
|
||||
|
||||
e(
|
||||
f
|
||||
)
|
||||
|
||||
1
|
||||
'''
|
||||
|
||||
eq 1, CoffeeScript.eval '''
|
||||
z = -> b: -> e: ->
|
||||
|
||||
z()
|
||||
.b
|
||||
c: 'd'
|
||||
.e()
|
||||
|
||||
f = [
|
||||
'g'
|
||||
]
|
||||
|
||||
1
|
||||
'''
|
||||
|
||||
eq 1, CoffeeScript.eval '''
|
||||
z = -> c: -> c: ->
|
||||
|
||||
z('b')
|
||||
.c 'a',
|
||||
{b: 'a'}
|
||||
.c()
|
||||
z(
|
||||
'b'
|
||||
)
|
||||
1
|
||||
'''
|
||||
|
||||
@@ -183,6 +183,69 @@ test "destructuring in function definition", ->
|
||||
url: '/home', async: true, beforeSend: fn, cache: true, method: 'post', data: {}
|
||||
}
|
||||
|
||||
test "rest element destructuring in function definition", ->
|
||||
obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
|
||||
|
||||
(({a, b, r...}) ->
|
||||
eq 1, a
|
||||
eq 2, b,
|
||||
deepEqual r, {c: 3, d: 4, e: 5}
|
||||
) obj
|
||||
|
||||
(({a: p, b, r...}, q) ->
|
||||
eq p, 1
|
||||
eq q, 9
|
||||
deepEqual r, {c: 3, d: 4, e: 5}
|
||||
) {a:1, b:2, c:3, d:4, e:5}, 9
|
||||
|
||||
a1={}; b1={}; c1={}; d1={}
|
||||
obj1 = {
|
||||
a: a1
|
||||
b: {
|
||||
'c': {
|
||||
d: {
|
||||
b1
|
||||
e: c1
|
||||
f: d1
|
||||
}
|
||||
}
|
||||
}
|
||||
b2: {b1, c1}
|
||||
}
|
||||
|
||||
(({a: w, b: {c: {d: {b1: bb, r1...}}}, r2...}) ->
|
||||
eq a1, w
|
||||
eq bb, b1
|
||||
eq r2.b, undefined
|
||||
deepEqual r1, {e: c1, f: d1}
|
||||
deepEqual r2.b2, {b1, c1}
|
||||
) obj1
|
||||
|
||||
b = 3
|
||||
f = ({a, b...}) ->
|
||||
f {}
|
||||
eq 3, b
|
||||
|
||||
(({a, r...} = {}) ->
|
||||
eq a, undefined
|
||||
deepEqual r, {}
|
||||
)()
|
||||
|
||||
(({a, r...} = {}) ->
|
||||
eq a, 1
|
||||
deepEqual r, {b: 2, c: 3}
|
||||
) {a: 1, b: 2, c: 3}
|
||||
|
||||
f = ({a, r...} = {}) -> [a, r]
|
||||
deepEqual [undefined, {}], f()
|
||||
deepEqual [1, {b: 2}], f {a: 1, b: 2}
|
||||
deepEqual [1, {}], f {a: 1}
|
||||
|
||||
f = ({a, r...} = {a: 1, b: 2}) -> [a, r]
|
||||
deepEqual [1, {b:2}], f()
|
||||
deepEqual [2, {}], f {a:2}
|
||||
deepEqual [3, {c:5}], f {a:3, c:5}
|
||||
|
||||
test "#4005: `([a = {}]..., b) ->` weirdness", ->
|
||||
fn = ([a = {}]..., b) -> [a, b]
|
||||
deepEqual fn(5), [{}, 5]
|
||||
@@ -362,3 +425,43 @@ test "Destructured parameter with default value, that itself has a default value
|
||||
y: 30
|
||||
radius: 30
|
||||
eq output, 'big-18-30-30'
|
||||
|
||||
test "#4566: destructuring with nested default values", ->
|
||||
f = ({a: {b = 1}}) ->
|
||||
b
|
||||
eq 2, f a: b: 2
|
||||
|
||||
test "#1043: comma after function glyph", ->
|
||||
x = (a=->, b=2) ->
|
||||
a()
|
||||
eq x(), undefined
|
||||
|
||||
f = (a) -> a()
|
||||
g = f ->, 2
|
||||
eq g, undefined
|
||||
h = f(=>, 2)
|
||||
eq h, undefined
|
||||
|
||||
test "#3845/#3446: chain after function glyph", ->
|
||||
angular = module: -> controller: -> controller: ->
|
||||
|
||||
eq undefined,
|
||||
angular.module 'foo'
|
||||
.controller 'EmailLoginCtrl', ->
|
||||
.controller 'EmailSignupCtrl', ->
|
||||
|
||||
beforeEach = (f) -> f()
|
||||
getPromise = -> then: -> catch: ->
|
||||
|
||||
eq undefined,
|
||||
beforeEach ->
|
||||
getPromise()
|
||||
.then (@result) =>
|
||||
.catch (@error) =>
|
||||
|
||||
doThing = -> then: -> catch: (f) -> f()
|
||||
handleError = -> 3
|
||||
eq 3,
|
||||
doThing()
|
||||
.then (@result) =>
|
||||
.catch handleError
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -439,6 +439,12 @@ test "#3216: For loop declaration as a value of an implicit object", ->
|
||||
arrayEq ob.b, test
|
||||
arrayEq ob.c, test
|
||||
arrayEq ob.d, test
|
||||
byFirstKey =
|
||||
a: for v in test by 1 then v
|
||||
arrayEq byFirstKey.a, test
|
||||
whenFirstKey =
|
||||
a: for v in test when true then v
|
||||
arrayEq whenFirstKey.a, test
|
||||
|
||||
test 'inline implicit object literals within multiline implicit object literals', ->
|
||||
x =
|
||||
@@ -587,3 +593,107 @@ test "#1263: Braceless object return", ->
|
||||
eq 1, obj.a
|
||||
eq 2, obj.b
|
||||
eq 3, obj.c()
|
||||
|
||||
test "#4564: indent should close implicit object", ->
|
||||
f = (x) -> x
|
||||
|
||||
arrayEq ['a'],
|
||||
for key of f a: 1
|
||||
key
|
||||
|
||||
g = null
|
||||
if f a: 1
|
||||
g = 3
|
||||
eq g, 3
|
||||
|
||||
h = null
|
||||
if a: (i for i in [1, 2, 3])
|
||||
h = 4
|
||||
eq h, 4
|
||||
|
||||
test "#4544: Postfix conditionals in first line of implicit object literals", ->
|
||||
two =
|
||||
foo:
|
||||
bar: 42 if yes
|
||||
baz: 1337
|
||||
eq 42, two.foo.bar
|
||||
eq 1337, two.foo.baz
|
||||
|
||||
f = (x) -> x
|
||||
|
||||
three =
|
||||
foo: f
|
||||
bar: 42 if yes
|
||||
baz: 1337
|
||||
eq 42, three.foo.bar
|
||||
eq 1337, three.foo.baz
|
||||
|
||||
four =
|
||||
f
|
||||
foo:
|
||||
bar: 42 if yes
|
||||
baz: 1337
|
||||
eq 42, four.foo.bar
|
||||
eq 1337, four.baz
|
||||
|
||||
x = bar: 42 if no
|
||||
baz: 1337
|
||||
ok not x?
|
||||
|
||||
# Example from #2051
|
||||
a = null
|
||||
_alert = (arg) -> a = arg
|
||||
_alert
|
||||
val3: "works" if true
|
||||
val: "hello"
|
||||
val2: "all good"
|
||||
eq a.val2, "all good"
|
||||
|
||||
test "#4579: Postfix for/while/until in first line of implicit object literals", ->
|
||||
two =
|
||||
foo:
|
||||
bar: x for x in [1, 2, 3]
|
||||
baz: 1337
|
||||
arrayEq [1, 2, 3], two.foo.bar
|
||||
eq 1337, two.foo.baz
|
||||
|
||||
f = (x) -> x
|
||||
|
||||
three =
|
||||
foo: f
|
||||
# Uncomment when #4580 is fixed
|
||||
# bar: x + y for x, y of a: 'b', c: 'd'
|
||||
bar: x + 'c' for x of a: 1, b: 2
|
||||
baz: 1337
|
||||
arrayEq ['ac', 'bc'], three.foo.bar
|
||||
eq 1337, three.foo.baz
|
||||
|
||||
four =
|
||||
f
|
||||
foo:
|
||||
"bar_#{x}": x for x of a: 1, b: 2
|
||||
baz: 1337
|
||||
eq 'a', four.foo[0].bar_a
|
||||
eq 'b', four.foo[1].bar_b
|
||||
eq 1337, four.baz
|
||||
|
||||
x = bar: 42 for y in [1]
|
||||
baz: 1337
|
||||
eq x.bar, 42
|
||||
|
||||
i = 5
|
||||
five =
|
||||
foo:
|
||||
bar: i while i-- > 0
|
||||
baz: 1337
|
||||
arrayEq [4, 3, 2, 1, 0], five.foo.bar
|
||||
eq 1337, five.foo.baz
|
||||
|
||||
i = 5
|
||||
six =
|
||||
foo:
|
||||
bar: i until i-- <= 0
|
||||
baz: 1337
|
||||
arrayEq [4, 3, 2, 1, 0], six.foo.bar
|
||||
eq 1337, six.foo.baz
|
||||
|
||||
|
||||
@@ -300,18 +300,16 @@ test "#4248: Unicode code point escapes", ->
|
||||
ok /a\u{12345}c/.test 'a\ud808\udf45c'
|
||||
|
||||
# rewrite code point escapes unless u flag is set
|
||||
input = """
|
||||
eqJS """
|
||||
/\\u{bcdef}\\u{abc}/u
|
||||
"""
|
||||
output = """
|
||||
""",
|
||||
"""
|
||||
/\\u{bcdef}\\u{abc}/u;
|
||||
"""
|
||||
eq toJS(input), output
|
||||
|
||||
input = """
|
||||
eqJS """
|
||||
///#{ 'a' }\\u{bcdef}///
|
||||
"""
|
||||
output = """
|
||||
""",
|
||||
"""
|
||||
/a\\udab3\\uddef/;
|
||||
"""
|
||||
eq toJS(input), output
|
||||
|
||||
@@ -107,6 +107,16 @@ test "#1183: super + closures", ->
|
||||
ret
|
||||
eq (new B).foo(), 10
|
||||
|
||||
test "#2331: bound super regression", ->
|
||||
class A
|
||||
@value = 'A'
|
||||
method: -> @constructor.value
|
||||
|
||||
class B extends A
|
||||
method: => super()
|
||||
|
||||
eq (new B).method(), 'A'
|
||||
|
||||
test "#3259: leak with @-params within destructured parameters", ->
|
||||
fn = ({@foo}, [@bar], [{@baz}]) ->
|
||||
foo = bar = baz = false
|
||||
|
||||
@@ -415,18 +415,16 @@ test "#4248: Unicode code point escapes", ->
|
||||
eq '\\u{123456}', "#{'\\'}#{'u{123456}'}"
|
||||
|
||||
# don't rewrite code point escapes
|
||||
input = """
|
||||
eqJS """
|
||||
'\\u{bcdef}\\u{abc}'
|
||||
"""
|
||||
output = """
|
||||
""",
|
||||
"""
|
||||
'\\u{bcdef}\\u{abc}';
|
||||
"""
|
||||
eq toJS(input), output
|
||||
|
||||
input = """
|
||||
eqJS """
|
||||
"#{ 'a' }\\u{bcdef}"
|
||||
"""
|
||||
output = """
|
||||
""",
|
||||
"""
|
||||
"a\\u{bcdef}";
|
||||
"""
|
||||
eq toJS(input), output
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# See http://wiki.ecmascript.org/doku.php?id=harmony:egal
|
||||
# See [http://wiki.ecmascript.org/doku.php?id=harmony:egal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
|
||||
egal = (a, b) ->
|
||||
if a is b
|
||||
a isnt 0 or 1/a is 1/b
|
||||
@@ -13,9 +13,20 @@ arrayEgal = (a, b) ->
|
||||
return no for el, idx in a when not arrayEgal el, b[idx]
|
||||
yes
|
||||
|
||||
exports.eq = (a, b, msg) -> ok egal(a, b), msg or "Expected #{a} to equal #{b}"
|
||||
exports.arrayEq = (a, b, msg) -> ok arrayEgal(a,b), msg or "Expected #{a} to deep equal #{b}"
|
||||
exports.eq = (a, b, msg) ->
|
||||
ok egal(a, b), msg or
|
||||
"Expected #{reset}#{a}#{red} to equal #{reset}#{b}#{red}"
|
||||
|
||||
exports.toJS = (str) ->
|
||||
CoffeeScript.compile str, bare: yes
|
||||
.replace /^\s+|\s+$/g, '' # Trim leading/trailing whitespace
|
||||
exports.arrayEq = (a, b, msg) ->
|
||||
ok arrayEgal(a,b), msg or
|
||||
"Expected #{reset}#{a}#{red} to deep equal #{reset}#{b}#{red}"
|
||||
|
||||
exports.eqJS = (input, expectedOutput, msg) ->
|
||||
actualOutput = CoffeeScript.compile input, bare: yes
|
||||
.replace /^\s+|\s+$/g, '' # Trim leading/trailing whitespace.
|
||||
|
||||
ok egal(expectedOutput, actualOutput), msg or
|
||||
"""Expected generated JavaScript to be:
|
||||
#{reset}#{expectedOutput}#{red}
|
||||
but instead it was:
|
||||
#{reset}#{actualOutput}#{red}"""
|
||||
|
||||
Reference in New Issue
Block a user