Compare commits

...

17 Commits

Author SHA1 Message Date
Geoffrey Booth
704924c3cd 2.0.0-beta3 (#4594)
* Don’t confuse the syntax highlighter

* Comment Assign::compilePatternMatch a bit

* Assignment expressions in conditionals are a bad practice

* Rename `wrapInBraces` to `wrapInParentheses`, to set the stage for future `wrapInBraces` that uses `{` and `wrapInBrackets` that uses `[`

* Correct comment

* object destructuring

* Allow custom position of the rest element.

* Output simple array destructuring assignments to ES2015

* Output simple object destructured assignments to ES2015

* Compile shorthand object properties to ES2015 shorthand properties

This dramatically improves the appearance of destructured imports.

* Don’t confuse the syntax highlighter

* Comment Assign::compilePatternMatch a bit

* Assignment expressions in conditionals are a bad practice

* Rename `wrapInBraces` to `wrapInParentheses`, to set the stage for future `wrapInBraces` that uses `{` and `wrapInBrackets` that uses `[`

* object destructuring

* Allow custom position of the rest element.

* rest element in object destructuring

* rest element in object destructuring

* fix string interpolation

* merging

* fixing splats in object literal

* Rest element in parameter destructuring

* merging with CS2

* merged with CS2

* Add support for the object spread initializer. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md

* Fix misspellings, trailing whitespace, other minor details

* merging with beta2

* refactor object spread properties

* small fix

* - Fixed object spread function parameters.
- Clean up "Assign" and moved all logic for object rest properties in single method (compileObjectDestruct).
- Add helper function "objectWithoutKeys" to the "UTILITIES" for use with object rest properties,
  e.g. {a, b, r...} = obj => {a, b} = obj, r = objectWithoutKeys(...)
- Clean up "Obj" and moved all logic for object spread properties in single method (compileSpread).
- Clean up "Code".
- Add method "hasSplat" to "Obj" and "Value" for checking if Obj contains the splat.
- Enable placing spread syntax triple dots on either right or left, per #85 (https://github.com/coffeescript6/discuss/issues/85)

* Fixed typos

* Remove unused code

* Removed dots (e.g. splat) on the left side from the grammar

* Initial release for deep spread properties, e.g. obj2 = {obj.b..., a: 1} or {obj[b][c]..., d: 7}
Tests need to be prepared!

* 1. Object literal spread properties

Object literals:
- obj = { {b:{c:{d:1}}}..., a:1 }

Parenthetical:
- obj = { ( body ), a:1 }
- obj = { ( body )..., a:1 }

Invocation:
- obj = { ( (args) -> ... )(params), a:1 }
- obj = { ( (args) -> ... )(params)..., a:1 }
- obj = { foo(), a:1 }
- obj = { foo()..., a:1 }

2. Refactor, cleanup & other optimizations.

* Merged with 2.0

* Cleanup

* Some more cleanup.

* Fixed error with freeVariable and object destructuring.

* Fixed errors with object spread properties.

* Improvements, fixed errors.

* Minor improvement.

* Minor improvements.

* Typo.

* Remove unnecessary whitespace.

* Remove unnecessary whitespace.

* Changed few "assertErrorFormat" tests since parentheses are now allowed in the Obj.

* Whitespace cleanup

* Comments cleanup

* fix destructured obj param declarations

* refine fix; add test

* Refactor function args ({a, b...})

* Additional tests for object destructuring in function argument.

* Minor improvement for object destructuring variable declaration.

* refactor function args ({a, b...}) and ({a, b...} = {}); Obj And Param cleanup

* fix comment

* Fix object destructuring variable declaration.

* more tests with default values

* fix typo

* Fixed default values in object destructuring.

* small fix

* Babel’s tests for object rest spread

* Style: spaces after colons in object declarations

* Cleanup comments

* Simplify Babel tests

* Fix comments

* Fix destructuring with splats in multiple objects

* Add test for default values in detsructuring assignment with splats

* Handle default values when assigning to object splats

* Rewrite traverseRest to fix handling of dynamic keys

* Fix double parens around destructuring with splats

* Update compileObjectDestruct comments

* Improve formatting of top-level destructures with splats and tidy parens

* Added a bigger destructuring-with-defaults test and fixed a bug

* Refactor destructuring grammar to allow additional forms

* Add a missing case to ObjSpreadExpr

* These tests shouldn’t run in the browser

* Fix test.html

* Fix docs scroll position getting screwed up by CodeMirror initialization

* Breaking change documentation about => (fixes #4593)

* Spread/rest syntax documentation

* Documentation about bound class methods

* 2.0.0-beta3 changelog

* Add note about ‘lib’

* Fix accidentally converting this to tabs

* Bump version to 2.0.0-beta3

* Update annotated source and test.html
2017-06-30 09:58:05 -07:00
Geoffrey Booth
a7a6006533 [CS2] Destructuring object spreads (#4493)
* Don’t confuse the syntax highlighter

* Comment Assign::compilePatternMatch a bit

* Assignment expressions in conditionals are a bad practice

* Rename `wrapInBraces` to `wrapInParentheses`, to set the stage for future `wrapInBraces` that uses `{` and `wrapInBrackets` that uses `[`

* Correct comment

* object destructuring

* Allow custom position of the rest element.

* Output simple array destructuring assignments to ES2015

* Output simple object destructured assignments to ES2015

* Compile shorthand object properties to ES2015 shorthand properties

This dramatically improves the appearance of destructured imports.

* Don’t confuse the syntax highlighter

* Comment Assign::compilePatternMatch a bit

* Assignment expressions in conditionals are a bad practice

* Rename `wrapInBraces` to `wrapInParentheses`, to set the stage for future `wrapInBraces` that uses `{` and `wrapInBrackets` that uses `[`

* object destructuring

* Allow custom position of the rest element.

* rest element in object destructuring

* rest element in object destructuring

* fix string interpolation

* merging

* fixing splats in object literal

* Rest element in parameter destructuring

* merging with CS2

* merged with CS2

* Add support for the object spread initializer. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md

* Fix misspellings, trailing whitespace, other minor details

* merging with beta2

* refactor object spread properties

* small fix

* - Fixed object spread function parameters.
- Clean up "Assign" and moved all logic for object rest properties in single method (compileObjectDestruct).
- Add helper function "objectWithoutKeys" to the "UTILITIES" for use with object rest properties,
  e.g. {a, b, r...} = obj => {a, b} = obj, r = objectWithoutKeys(...)
- Clean up "Obj" and moved all logic for object spread properties in single method (compileSpread).
- Clean up "Code".
- Add method "hasSplat" to "Obj" and "Value" for checking if Obj contains the splat.
- Enable placing spread syntax triple dots on either right or left, per #85 (https://github.com/coffeescript6/discuss/issues/85)

* Fixed typos

* Remove unused code

* Removed dots (e.g. splat) on the left side from the grammar

* Initial release for deep spread properties, e.g. obj2 = {obj.b..., a: 1} or {obj[b][c]..., d: 7}
Tests need to be prepared!

* 1. Object literal spread properties

Object literals:
- obj = { {b:{c:{d:1}}}..., a:1 }

Parenthetical:
- obj = { ( body ), a:1 }
- obj = { ( body )..., a:1 }

Invocation:
- obj = { ( (args) -> ... )(params), a:1 }
- obj = { ( (args) -> ... )(params)..., a:1 }
- obj = { foo(), a:1 }
- obj = { foo()..., a:1 }

2. Refactor, cleanup & other optimizations.

* Merged with 2.0

* Cleanup

* Some more cleanup.

* Fixed error with freeVariable and object destructuring.

* Fixed errors with object spread properties.

* Improvements, fixed errors.

* Minor improvement.

* Minor improvements.

* Typo.

* Remove unnecessary whitespace.

* Remove unnecessary whitespace.

* Changed few "assertErrorFormat" tests since parentheses are now allowed in the Obj.

* Whitespace cleanup

* Comments cleanup

* fix destructured obj param declarations

* refine fix; add test

* Refactor function args ({a, b...})

* Additional tests for object destructuring in function argument.

* Minor improvement for object destructuring variable declaration.

* refactor function args ({a, b...}) and ({a, b...} = {}); Obj And Param cleanup

* fix comment

* Fix object destructuring variable declaration.

* more tests with default values

* fix typo

* Fixed default values in object destructuring.

* small fix

* Babel’s tests for object rest spread

* Style: spaces after colons in object declarations

* Cleanup comments

* Simplify Babel tests

* Fix comments

* Fix destructuring with splats in multiple objects

* Add test for default values in detsructuring assignment with splats

* Handle default values when assigning to object splats

* Rewrite traverseRest to fix handling of dynamic keys

* Fix double parens around destructuring with splats

* Update compileObjectDestruct comments

* Improve formatting of top-level destructures with splats and tidy parens

* Added a bigger destructuring-with-defaults test and fixed a bug

* Refactor destructuring grammar to allow additional forms

* Add a missing case to ObjSpreadExpr

* These tests shouldn’t run in the browser

* Fix test.html
2017-06-29 22:57:42 -07:00
Julian Rosse
58c608620e disallow inline chained call after func glyph (#4590) 2017-06-29 19:00:01 -07:00
Julian Rosse
b1d2061cbc [CS2] Fix #3906: handle indentation inside indented chain (#4587)
* safe missing end token error [Fixes #3906]

* handle indentation inside indented chain [Fixes #3906]
2017-06-29 18:39:05 -07:00
Geoffrey Booth
1f31073201 [CS2] JSX documentation, Try CoffeeScript improvements (#4583)
* Update browser compiler

* Argument parsing tests require CommonJS environment

* JSX section in the docs

* Breaking change note for < and > operators

* Fix JSX example

* Try CoffeeScript improvements: set the hash automatically, remove ‘link’ button, automatically save code in localStorage

* Fix the code editors’ handling of tab-indented code

* Fix JSX example to work with React

* Compiled, not rendered
2017-06-25 23:53:35 -07:00
Julian Rosse
a3a1fb0dd7 [CS2] Fix for comma after function glyph [#1043] (#4588)
* comma after function glyph [#1043]

* test fat arrow

* chain after function glyph [#3446] [#3845]
2017-06-25 23:46:54 -07:00
Julian Rosse
26f6fa6570 [CS2] destructured nested defaults [fixes #4566] (#4574)
* destructured nested defaults [fixes #4566]

* don't parenthesize nested assigns
2017-06-20 21:53:37 -07:00
Julian Rosse
0619a7a76c Fix #4579: postfix for in object literals (#4581)
* postfix for on first line of implicit object

* issue numbers

* handle until and while similarly
2017-06-20 21:50:50 -07:00
Danny McClanahan
31cd782ba7 [CS2] CLI: Test option parsing for current CLI (#4565)
* intermediate save

* add note saying where OptionParser is used in coffee command

* add some more work

* fix flatten functions

* refactor tests

* add basic test

* remove unused file

* compilation now hangs

* remove unnecessary changes

* add tests!!!

* clarify a test
2017-06-15 09:09:02 -07:00
Julian Rosse
5e90b224c2 Fix #4564: indent closes implicit object (#4570)
* indent closes implicit object [Fixes #4564]

* add test for just implicit object

* remove stray lib file
2017-06-15 09:07:36 -07:00
Julian Rosse
9a48566b24 [CS2] Restore bound class methods via runtime check to avoid premature calling of bound method before binding (#4561)
* bound method runtime check

* restore bound method tests

* bound method tests

* test bound method in prop-named class

* run check against parent class

* dummy commit

* remove comment

* rename to boundMethodCheck

* fixes from code review

* use ref to own class for check

* fixes from code review

* remove unneeded param
2017-06-14 15:11:53 -07:00
Chris Connelly
76e70a6c81 Fix immediately invoked named classes (#4569)
Fixes #4436.
2017-06-12 23:11:39 -07:00
Geoffrey
5b7a7779fb Merge branch 'master' of github.com:jashkenas/coffeescript into 2 2017-06-08 21:57:01 -07:00
Michal Srb
48c7debe5a Fix REPL assignment (#4559) 2017-06-07 11:37:25 -07:00
Michal Srb
dc0fb85fd3 [CS2] Support for CSX - equivalent of JSX (#4551)
* CSX implementation

* fixed comment, used toJS, added error tests, fixed error in identifier regex, fixed interpolation inside attributes value and added test

* added missing test for bare attributes, split attribute and indentifier regex, fixed checking for closing tags closing angle bracket

* Refactor tests that compare expected generated JavaScript with actual generated JavaScript to use common helper; add colors to error message to make differences easier to read

* Better match the style of the rest of the codebase

* Remove unused function

* More style fixes

* Allow unspaced less-than operator when not using CSX

* Replace includesCSX with a counter and simplify the unspaced operator logic

* Fixed indexing and realized that I completely enabled the tight spacing, added a test for it too

* Style fixes
2017-06-06 23:33:46 -07:00
Julian Rosse
63b109a4f5 Fix #4544: postfix conditional on first line of implicit object (#4554)
* support postfix conditional on first line of implicit object

* cleanup cleanup

* also fixes #2051, added test
2017-05-29 17:29:45 -07:00
Geoffrey Booth
cd6daf0b79 2.0.0-beta2 docs 2017-05-16 18:59:02 -07:00
70 changed files with 6919 additions and 1973 deletions

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ test/*.js
parser.output
/node_modules
npm-debug.log*
yarn.lock

View File

@@ -6,4 +6,4 @@
* Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/coffeescript/tree/master/src). If youre just getting started with CoffeeScript, theres 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. Well 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. Well do those things before cutting a new release. You _should,_ however, commit the updated compiled JavaScript files in `lib`.

View File

@@ -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) ->

View File

@@ -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">-&gt;</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">-&gt;</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> = -&gt;</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> = -&gt;</span>
printLine (<span class="hljs-keyword">new</span> optparse.OptionParser SWITCHES, BANNER).help()</pre></div></div>
printLine optionParser.help()</pre></div></div>
</li>

View File

@@ -446,6 +446,7 @@ token stream.</p>
Identifier: [
o <span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-function">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">&#182;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-24">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-25">&#182;</a>
</div>
<p>The <strong>Code</strong> node is the function literal. Its 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">&#182;</a>
<a class="pilcrow" href="#section-26">&#182;</a>
</div>
<p>CoffeeScript has two different symbols for functions. <code>-&gt;</code> is for ordinary
functions, and <code>=&gt;</code> is for functions bound to the current value of <em>this</em>.</p>
@@ -651,11 +687,11 @@ functions, and <code>=&gt;</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">&#182;</a>
<a class="pilcrow" href="#section-27">&#182;</a>
</div>
<p>An optional, trailing comma.</p>
@@ -669,11 +705,11 @@ functions, and <code>=&gt;</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">&#182;</a>
<a class="pilcrow" href="#section-28">&#182;</a>
</div>
<p>The list of parameters that a function accepts can be of any length.</p>
@@ -690,11 +726,11 @@ functions, and <code>=&gt;</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">&#182;</a>
<a class="pilcrow" href="#section-29">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-30">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-31">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-32">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-33">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-34">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-35">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-36">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-37">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-38">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-39">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-40">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-41">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-42">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-43">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-44">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-45">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-46">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-47">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-48">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-49">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-50">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-51">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-52">&#182;</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 wouldnt make sense.</p>
</li>
<li id="section-52">
<li id="section-53">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-52">&#182;</a>
<a class="pilcrow" href="#section-53">&#182;</a>
</div>
<p>The variants of <em>try/catch/finally</em> exception handling blocks.</p>
@@ -1248,11 +1284,11 @@ having the newlines wouldnt make sense.</p>
</li>
<li id="section-53">
<li id="section-54">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-53">&#182;</a>
<a class="pilcrow" href="#section-54">&#182;</a>
</div>
<p>A catch clause names its error and runs a block of code.</p>
@@ -1267,11 +1303,11 @@ having the newlines wouldnt make sense.</p>
</li>
<li id="section-54">
<li id="section-55">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-54">&#182;</a>
<a class="pilcrow" href="#section-55">&#182;</a>
</div>
<p>Throw an exception object.</p>
@@ -1284,11 +1320,11 @@ having the newlines wouldnt make sense.</p>
</li>
<li id="section-55">
<li id="section-56">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-55">&#182;</a>
<a class="pilcrow" href="#section-56">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-57">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-58">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-59">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-60">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-61">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-62">&#182;</a>
</div>
<p>The source of a comprehension is an array or object with an optional guard
clause. If its 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">&#182;</a>
<a class="pilcrow" href="#section-63">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-64">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-65">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-66">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-67">&#182;</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">&#182;</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">&#182;</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">&#182;</a>
</div>
</div>
</li>
<li id="section-70">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-70">&#182;</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">&#182;</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">&#182;</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">&#182;</a>
</div>
</div>
</li>
<li id="section-73">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-73">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-74">&#182;</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

View File

@@ -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>

View File

@@ -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

View File

@@ -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> -&gt;</span>
detectEnd: <span class="hljs-function"><span class="hljs-params">(i, condition, action, opts = {})</span> -&gt;</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 &lt; <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 &lt; <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">&#182;</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">-&gt;</span>
<span class="hljs-function"> <span class="hljs-title">condition</span> = <span class="hljs-params">(token, i)</span> -&gt;</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> -&gt;</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) -&gt;
@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">&#182;</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) -&gt;
[tag] = token
[prevTag] = prevToken = <span class="hljs-keyword">if</span> i &gt; <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 &lt; 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 &lt; 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> = -&gt;</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> = -&gt;</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> -&gt;</span>
idx = j ? i
<span class="hljs-title">startImplicitCall</span> = <span class="hljs-params">(idx)</span> -&gt;</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> = -&gt;</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> -&gt;</span>
idx = j ? i
<span class="hljs-title">startImplicitObject</span> = <span class="hljs-params">(idx, startsLine = <span class="hljs-literal">yes</span>)</span> -&gt;</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> -&gt;</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> =&gt;</span>
nextTerminatorIdx = <span class="hljs-literal">null</span>
@detectEnd j,
<span class="hljs-function"><span class="hljs-params">(token)</span> -&gt;</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span>
(token, i) -&gt; 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">&#182;</a>
</div>
<p>Dont end an implicit call on next indent if any of these are in an argument</p>
<p>Dont 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">'=&gt;'</span>, <span class="hljs-string">'-&gt;'</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">'=&gt;'</span>, <span class="hljs-string">'-&gt;'</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 &gt; <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>Dont 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">&#182;</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">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-27">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-28">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-29">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-30">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-31">&#182;</a>
</div>
<p>Close implicit objects when at end of line, line didnt end with a comma
and the implicit object didnt start the line or the next line doesnt 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">&#182;</a>
<a class="pilcrow" href="#section-32">&#182;</a>
</div>
<p>Close implicit object if comma is the last character
and what comes after doesnt 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">&#182;</a>
<a class="pilcrow" href="#section-33">&#182;</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">&#182;</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">-&gt;</span>
@scanTokens (token, i, tokens) -&gt;
<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">'-&gt;'</span>, <span class="hljs-string">'=&gt;'</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">&#182;</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

View File

@@ -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>&emsp;
<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>&lt;</code> and <code>&gt;</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>“Its 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>Whats 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>=&gt;</code> becomes an ES <code>=&gt;</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 CoffeeScripts 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, its possible to <code>extend</code> an ES class; that wasnt 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 ES2017s <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 ES2017s <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 Theres ES2015?</h3>
<p>CoffeeScript introduced many new features to the JavaScript world, such as <a href="#fat-arrow"><code>=&gt;</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 youre 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>=&gt;</code> becomes an ES <code>=&gt;</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 2s 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 2s 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 youre 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 &gt; app.js
coffee --print *.coffee | babel --presets env &gt; app.js
</code></pre>
</blockquote><h3>Node.js</h3>
<p>If youd 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>&ensp;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>&ensp;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/">Babels rest spread transform</a>; but once it is supported, we will revise the compilers 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>&lt;</code> and <code>&gt;</code>. You can interpolate CoffeeScript code inside a tag using <code>{</code> and <code>}</code>. To avoid compiler errors, when using <code>&lt;</code> and <code>&gt;</code> to mean “less than” or “greater than,” you should wrap the operators in spaces to distinguish them from XML tags. So <code>i &lt; len</code>, not <code>i&lt;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>=&gt;</code> compiled to a regular <code>function</code> but with references to <code>this</code>/<code>@</code> rewritten to use the outer scopes <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>=&gt;</code> compiles to <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">ES2015s <code>=&gt;</code></a>, which behaves slightly differently. The largest difference is that in ES2015, <code>=&gt;</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>&ensp;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">-&gt;</span> <span class="keyword">this</span> <span class="comment"># Throws a compiler error</span>
</code></pre>
</blockquote><p>Class methods cant be bound (i.e. you cant 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">=&gt;</span> <span class="comment"># Throws a compiler error</span>
</blockquote><p>ES2015 classes dont allow bound (fat arrow) methods. The CoffeeScript compiler goes through some contortions to preserve support for them, but one thing that cant 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">-&gt;</span>
<span class="keyword">super</span>()
@method = <span class="function">=&gt;</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">=&gt;</span>
<span class="built_in">console</span>.log <span class="string">'Clicked!'</span>, @
</code></pre>
</blockquote><p>Class methods cant 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>&lt;</code> and <code>&gt;</code> Operators</h3>
<p>With the addition of <a href="#jsx">JSX</a>, the <code>&lt;</code> and <code>&gt;</code> characters serve as both the “less than” and “greater than” operators and as the delimiters for XML tags, like <code>&lt;div&gt;</code>. For best results, in general you should always wrap the operators in spaces to distinguish them from XML tags: <code>i &lt; len</code>, not <code>i&lt;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"> &mdash; <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 its 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 users 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 mightve thrown off our vertical scroll position
document.getElementById(window.location.hash.slice(1)).scrollIntoView()
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
popular = ['pepperoni', 'sausage', 'cheese']
unwanted = ['anchovies', 'olives']
all = [popular..., unwanted..., 'mushrooms']

View File

@@ -0,0 +1,5 @@
outer = ->
inner = => Array.from arguments
inner()
outer(1, 2) # Returns '' in CoffeeScript 1.x, '1, 2' in CoffeeScript 2

View 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>

View File

@@ -0,0 +1,5 @@
user =
name: 'Werner Heisenberg'
occupation: 'theoretical physicist'
currentUser = { user..., status: 'Uncertain' }

View File

@@ -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 ', '}
"""

View File

@@ -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

View 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 scopes `this`, or with the inner function bound to the outer scope via `.bind` (hence the name “bound function”). In CoffeeScript 2, `=>` compiles to [ES2015s `=>`](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)')
```

View File

@@ -16,15 +16,18 @@ class B extends A
constructor: -> this # Throws a compiler error
```
Class methods cant be bound (i.e. you cant define a class method using a fat arrow) though you can define such methods in the constructor instead:
ES2015 classes dont allow bound (fat arrow) methods. The CoffeeScript compiler goes through some contortions to preserve support for them, but one thing that cant 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 cant be used with `new` (uncommon):

View File

@@ -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.

View File

@@ -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')
```

View File

@@ -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, its possible to `extend` an ES class; that wasnt 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 ES2017s [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 ES2017s [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.

View 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.

View File

@@ -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 [Babels rest spread transform](https://babeljs.io/docs/plugins/transform-object-rest-spread/); but once it is supported, we will revise the compilers 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.

View File

@@ -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 youre 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 2s 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 2s 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 youre 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

View File

@@ -39,6 +39,7 @@
<script type="text/coffeescript">
@testingBrowser = yes
@global = window
bold = red = green = reset = ''
stdout = document.getElementById 'stdout'
start = new Date
@currentFile = ''

View File

@@ -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>

View File

@@ -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 its 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 users 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 mightve thrown off our vertical scroll position
document.getElementById(window.location.hash.slice(1)).scrollIntoView()

View File

@@ -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>&lt;</code> and <code>&gt;</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>

View File

@@ -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>&emsp;
<button type="button" class="btn btn-primary" data-action="link" data-example="try-coffeescript"><%= include('documentation/images/link.svg') %></button>
</div>
</div>
</aside>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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'];

View File

@@ -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';
},

View File

@@ -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

View File

@@ -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;

View File

@@ -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),

View File

@@ -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);

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.0-beta2
// Generated by CoffeeScript 2.0.0-beta3
(function() {
var Scope,
indexOf = [].indexOf;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.0-beta2
// Generated by CoffeeScript 2.0.0-beta3
(function() {
var LineMap, SourceMap;

View File

@@ -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"

View File

@@ -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 = ->

View File

@@ -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
# ----------

View File

@@ -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.
#

View File

@@ -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 were 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 were 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 isnt 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 wouldnt have been added yet since it was skipped earlier.
# Add this parameter to the scope, since it wouldnt 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'

View File

@@ -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:

View File

@@ -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

View File

@@ -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']

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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 Friends
# [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> &&&&euro; &#8364; &#x20AC;;; </Person>
''', '''
<Person> &&&&euro; &#8364; &#x20AC;;; </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;
'''

View File

@@ -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
^^
'''

View File

@@ -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
'''

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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}"""