Compare commits

...

71 Commits
2.0.2 ... 2.3.0

Author SHA1 Message Date
Geoffrey Booth
8061ecf0e2 2.3.0 (#5043)
* Move analytics initialization into docs.coffee

* Bump Bootstrap and CodeMirror versions

* Update output

* Merge CodeMirror styles

* Update CodeMirror styles

* Better flexbox layout for docs, including flex auto-sized sidebar and main column

* Minor styling fixes for Edge

* Fix scrollspy for new layout

* Update output

* Clicking on the CoffeeScript logo in the navbar should scroll to top; fix main column width on mobile

* Update output

* 2.3.0 changelog

* Update operators section for **

* Update docs for ES2018 object rest/spread

* Remove references to Node's experimental modules support

* Move the README coffee cup to the right side, so our name isn't cut off on the NPM page; update README and CONTRIBUTING text

* Update dependencies

* Add compatibility note for async generators

* Bump version to 2.3.0 and update output

* Have CI test in Node 10 instead of 9

* Somehow this gets generated slightly differently in Node 10

* Fix #4042: Update broken link
2018-04-28 18:57:15 -07:00
Geoffrey Booth
6df8f5ff17 Remove references to Node's experimental modules support 2018-04-27 10:27:20 -07:00
Geoffrey Booth
776e7e5328 Docs improvements (#5042)
* Move analytics initialization into docs.coffee

* Bump Bootstrap and CodeMirror versions

* Update output

* Merge CodeMirror styles

* Update CodeMirror styles

* Better flexbox layout for docs, including flex auto-sized sidebar and main column

* Minor styling fixes for Edge

* Fix scrollspy for new layout

* Update output

* Clicking on the CoffeeScript logo in the navbar should scroll to top; fix main column width on mobile

* Update output
2018-04-27 10:07:01 -07:00
zdenko
871079b25c Fix #4876: remove polyfill for object rest/spread (#4884)
* object rest/spread

* merge branch 'master' into 'object_rest_spread'

* explore

* Revert "merge branch 'master' into 'object_rest_spread'"

This reverts commit eeeffef55d.

* Revert "explore"

This reverts commit d21cd72aa3.

* split tests

* improvements

* unassignable rest property

* cleanup

* formatting, cleanup

* handle non-final object-destructured splat

* merge with @helixbass's branch
2018-04-23 09:50:42 -07:00
Ruben Bridgewater
be2b24310f Fix: a test relied on faulty behavior (#5028)
`assert.throws` did not test for the error message so far. This
changes it to actually test for the error message.
2018-04-10 09:21:40 -07:00
zdenko
1f9cd4eaf7 Fix #4875: Asynchronous iterators (#4893)
* async iterators

* tests; refactor 'For' grammar rules

* async iterator tests

* formatting
2018-04-08 13:42:54 -07:00
Geoffrey Booth
47c491ffa1 Regular expressions s (dotAll) flag (#4880)
* Support ES2018 regex dotall flag

* Test in Node 9

* Alphabetize regex flags

* Split regex dotall test into its own file, use new feature detection method of loading it

* Update docs to explain compatibility for ES2018 and newer Node features

* Remove unnecessary paragraph
2018-03-30 15:25:41 -07:00
Geoffrey Booth
195a46ab77 Fix #4877: Exponentiation operators (#4881)
* Passthrough exponentiation operator; remove tests that are invalid JavaScript

* Treat **= as a passthrough assignment

* Get tests passing in Node 6

* Improve scoping

* Move exponentiation tests into their own file, now that it's filtered out by Cakefile

* Restore original test
2018-03-30 00:47:40 -07:00
Geoffrey Booth
e5aa758dda 2.2.4 (#5025)
* Update changelog for 2.2.4

* Bump version to 2.2.4; update output
2018-03-29 20:00:45 -07:00
zdenko
001f97ac39 Fix #5013: return statement as an expression (#5014)
* fix #5013

* disallow statement in the expression
2018-03-17 23:08:43 -07:00
Robert de Forest
ce66a499de Make node --harmony bin/cake test pass on Node 9 (#5012)
* Make `node --harmony bin/cake test` pass on Node 9

Make classMaker() explicitly return an object

* Update Travis and AppVeyor to use --harmony and v9
2018-03-17 23:02:40 -07:00
zdenko
0268505119 Optimize 'for-range-by' loop when 'by' is literal number (#5016)
* optimize 'for-range-by' loop when 'by' is literal number

* small fix
2018-03-15 08:02:13 -07:00
Robert de Forest
5a43b2d7c5 simplified test file skipping (#4996) (#5003)
simplified test file skipping (#4996)
2018-03-12 19:03:44 -07:00
Geoffrey Booth
820942c3d0 2.2.3 (#5010)
* Bump version to 2.2.3; update packages

* Update output

* Update changelog
2018-03-11 13:48:14 -07:00
zdenko
1869f3121d Fix: destructuring assignment with an empty array in object (#5000)
* destructuring assignment with empty array in object

* improvements
2018-03-10 07:39:35 -08:00
zdenko
4c0363fb7c Fix #5004: incorrect compiled code when a destructuring array contains accessors (#5005) 2018-03-07 15:57:31 -08:00
Geoffrey Booth
746b0c7cc6 Code of Conduct (#4992)
* Code of Conduct

* Email address
2018-02-26 22:18:13 -08:00
Geoffrey Booth
b2fe7772c5 Pull request template (#4994) 2018-02-26 17:50:25 -08:00
Geoffrey Booth
23479eb486 Create issue template (#4993) 2018-02-26 17:50:14 -08:00
Geoffrey Booth
63b8543d73 Update output 2018-02-25 01:09:52 -08:00
zdenko
7542a75ff8 get/set example (#4990) 2018-02-25 00:18:57 -08:00
Joseph Lin
0875039d52 [update] copyright year to 2018 (#4988) 2018-02-24 23:40:11 -08:00
Geoffrey Booth
e0833c29ce 2.2.2 (#4986)
* Changelog for 2.2.2

* Bump version to 2.2.2

* Bump dependencies

* Update output
2018-02-22 00:31:14 -08:00
zdenko
72ab6feb2f Fix #4889: for...range loop condition (#4891)
* fix #4889

* test

* move test from 'control_flow' to 'ranges'

* More range tests
2018-02-20 00:46:20 -08:00
zdenko
eb7009268d fix #4898 (#4899) 2018-02-17 16:57:49 -08:00
zdenko
571e9df335 fix #4874 (#4888) 2018-02-08 21:11:11 -08:00
Geoffrey Booth
a73f66bc11 2.2.1 (#4885)
* 2.2.1 changelog

* Bump version to 2.2.1 and update output
2018-02-06 22:28:15 -08:00
zdenko
ba094126e2 Fix #4882: Range not declaring var for the "i" (#4883)
* fix #4882

* test
2018-02-04 13:33:08 -08:00
zdenko
794f65fbd7 Fix #4878: Compile error when using destructuring with a splat or expansion in an array (#4879)
* fix #4878

* improvements

* test

* refactor
2018-02-03 13:35:41 -08:00
Geoffrey Booth
0490eb9a3a 2.2.0 (#4873)
* Changelog for 2.2.0

* Bump version

* Bump package versions

* Fix `cake doc:source` for Docco 0.8.0

* Update output; some text tweaks
2018-02-01 20:14:26 -08:00
zdenko
9e80f6fa67 Fix #2047: Infinite loop possible when for loop with range uses variables (#4853)
* fix #2047

* Additional check for 'step'; tests

* Fix #4105 (#4855)

* Update output

* Throw warning for unsupported runtimes, e.g. Node < 6 (#4839)

* fix #1403 (#4854)

* Update output

* [Change]: Destructuring with non-final spread should still use rest syntax (#4517) (#4825)

* destructuring optimization

* refactor

* minor improvement, fix errors

* minor refactoring

* improvements

* Update output

* Update output

* Fix #4843: bad output when assigning to @prop in destructuring assignment with defaults (#4848)

* fix #4843

* improvements

* typo

* small fix

* Fix #3441: parentheses wrapping expression throw invalid error  (#4849)

* fix #3441

* improvements

* refactor

* Fix #1726: expression in property access causes unexpected results (#4851)

* fix #1726

* Explain what's happening, rather than just linking to an issue

* Updated output

* Optimization

* Update output

* remove casting to number

* cleanup tests
2018-01-31 19:51:43 -08:00
Geoffrey Booth
fc5ab1afa3 Update output 2018-01-31 06:33:17 -08:00
zdenko
0aa77f157a fix #4871 (#4872) 2018-01-31 06:31:52 -08:00
Geoffrey Booth
1849f0db76 Fix #4852, @get @set implicit object (#4867)
* Test cases for #4852, get/set oddities

* Fix soak before accessor call to `get` or `set`

* Fixes #4852: More get/set cases; cleanup style

* Check for tokens' existence before referencing them
2018-01-30 21:17:55 -08:00
zdenko
d86597dbad Fix #3306: trailing comma in a function call in the last line throws a syntax error (#4860)
* fix #3306

* added test
2018-01-30 21:09:50 -08:00
zdenko
bf91781888 Fix #4868: Incorrect ‘Can’t call super with @params’ error (#4869)
* fix #4868

* improvements

* tests
2018-01-30 21:06:54 -08:00
Geoffrey Booth
dcd6de9a77 Update output 2018-01-30 20:51:15 -08:00
zdenko
70b510bcb7 Fix #3933: Missing then doesn’t trigger nearby error (#4862)
* fix #3933

* tests
2018-01-30 15:04:29 -08:00
zdenko
84d596d534 Fix #4437: variable scope in chained calls (#4863)
* fix #4437

* tests
2018-01-29 23:01:25 -08:00
zdenko
c804087ec9 Fix #3921 & #2342: inline function without parentheses used in condition, inline 'else' (#4838)
* fix #3921: inline function without parentheses used in condition

* add grammar rules

* fix issue #2343

* typos

* multiple 'else' in line

* close 'else if'
2018-01-28 17:51:57 -08:00
zdenko
74bd537d6c fix #3909 (#4861) 2018-01-28 02:38:39 -08:00
zdenko
0217ed5505 Fix #1726: expression in property access causes unexpected results (#4851)
* fix #1726

* Explain what's happening, rather than just linking to an issue

* Updated output

* Optimization
2018-01-15 20:03:01 -08:00
zdenko
e53307b46b Fix #3441: parentheses wrapping expression throw invalid error (#4849)
* fix #3441

* improvements

* refactor
2018-01-15 19:32:27 -08:00
zdenko
c8c8c167d1 Fix #4843: bad output when assigning to @prop in destructuring assignment with defaults (#4848)
* fix #4843

* improvements

* typo

* small fix
2018-01-15 19:29:48 -08:00
zdenko
c1283ead45 [Change]: Destructuring with non-final spread should still use rest syntax (#4517) (#4825)
* destructuring optimization

* refactor

* minor improvement, fix errors

* minor refactoring

* improvements

* Update output

* Update output
2018-01-15 19:24:21 -08:00
Geoffrey Booth
3ade25a32c Update output 2018-01-15 19:06:57 -08:00
zdenko
7ea3ea92f3 fix #1403 (#4854) 2018-01-15 16:54:55 -08:00
Geoffrey Booth
a70f1a082e Throw warning for unsupported runtimes, e.g. Node < 6 (#4839) 2018-01-14 23:40:36 -08:00
Geoffrey Booth
2ccbb6b91a Update output 2018-01-14 20:56:26 -08:00
zdenko
e348026073 Fix #4105 (#4855) 2018-01-14 20:16:29 -08:00
Geoffrey Booth
7c6f3788ca Fix #4836: functions named get or set can be used without parentheses when attached to @ (#4837) 2018-01-06 13:58:43 -08:00
Geoffrey Booth
12fcbfc654 2.1.1 (#4835)
* Bump version to 2.1.1

* 2.1.1 changelog

* 2.1.1 updated output
2017-12-29 16:54:57 -08:00
Chris Connelly
38f5963b85 Use free variables for anonymous classes (#4826)
Fixes #4822.
2017-12-28 14:50:12 -08:00
Chris Connelly
ffbe51e374 Set the context of executable class body wrappers (#4828)
Fixes #4827.
2017-12-18 10:51:08 -08:00
Geoffrey Booth
926cb8463c 2.1.0 (#4823)
* Changelog for 2.1.0; remove text from objects section that is no longer valid for CS2/ES2015.

* Update packages

* 2.1.0 build

* Update output

* Correct reference to `//` division, per https://github.com/coffeescript6/discuss/issues/39#issuecomment-347144647
2017-12-10 23:12:33 -08:00
zdenko
b1286752b9 improve elision output (#4824) 2017-12-10 15:22:34 -08:00
zdenko
5eb9dded52 [Enhancement] Computed property keys (#4622) (#4803)
* Computed property keys

* refactor

* improvements

* refactor tests

* fix comments
2017-12-08 22:42:47 -08:00
zdenko
7f5ab7eb0d [Enhancement] CSX fragments syntax (#4802) (#4804)
* CSX fragments

* regex improvement; tests

* regex improvement

* regex improvement

* bug fix; regex

* Fix style

* Split fragment tests
2017-12-08 22:41:02 -08:00
zdenko
5bc85b8f6d Fix #4798: Incorrect output for object rest destructuring inside array destructuring (#4799)
* fix #4798

* additional tests
2017-12-04 08:00:45 -08:00
zdenko
81d1b511f0 Fix #4811: Heregex comments cannot contain three slashes in a row (#4814)
* fix comment with /// in heregex

* cleanup

* improved pattern for comments in heregex; fixed unescaped hashes in lexer.coffee

* improvements

* refactor RegExp

* cleanup

* Improved RegEx
2017-12-03 11:55:26 -08:00
Geoffrey Booth
2dc3f6c87a Cache node_modules for AppVeyor builds (#4817) 2017-12-02 20:49:22 -08:00
Geoffrey Booth
04959162c6 AppVeyor (#4812)
* First attempt at using AppVeyor

* Maybe it doesn't like the leading dot?

* Versions are an array

* Fix tests on Windows: for some reason, Windows requires `coffee` to be executed as `node coffee` (in the context of `spawnSync`, at least)

* Use npm@latest, see if that's less noisy

* Nevermind, only an issue on Node 6

* Better to see the NPM version before npm install
2017-12-01 13:19:48 -08:00
zdenko
f14c7ffa3f Fixes #4684: Elision (#4796)
* Elision

* test

* improvements

* grammar optimization

* cleanup
2017-11-28 21:24:18 -08:00
Geoffrey Booth
64b8dd486a 2.0.3 (#4795)
* Fix #4775: Remove no-longer-correct mention of the docs using text/coffeescript

* 2.0.3 changelog

* Update output for 2.0.3

* Bump date
2017-11-26 19:29:08 -08:00
Geoffrey Booth
7864acabc3 Fix #4790: Double-check that we're not creating a bound generator function, even if the yield got stuffed inside a compiler-generated IIFE (#4792) 2017-11-21 17:14:32 -08:00
zdenko
9812d28748 Fix #4787: Destructuring of objects within arrays can generate invalid JavaScript (#4791)
* Fix #4787

* simplify condition
2017-11-20 20:00:27 -08:00
zdenko
555c22af58 fix #4580 (#4793) 2017-11-20 09:15:19 -08:00
Geoffrey Booth
3f4b03bcff Fix #4763: Comments at beginning or end of REPL input shouldn't throw errors (#4764) 2017-11-19 11:37:08 -08:00
Geoffrey Booth
a706a64a6d Fix #4765: .map file should always have the same output base filename as the generated .js file (#4784) 2017-11-16 13:30:40 -08:00
Geoffrey Booth
637fe305a6 Fix #4774: export default followed by an object should always work, even if the object contains braces. default shouldn't suppress a newline, we should handle it in the grammar the same way returning an implicit object is handled (#4783) 2017-11-15 21:38:05 -08:00
Geoffrey Booth
bd824c73dd Fix #4780: Don't mutate the options object when compiling and transpiling, so that options are correct on subsequent iterations (#4785) 2017-11-14 08:13:22 -08:00
94 changed files with 11694 additions and 5910 deletions

View File

@@ -3,6 +3,7 @@ language: node_js
node_js:
- 6
- 8
- 10
cache:
directories:
@@ -14,5 +15,6 @@ script:
- node ./bin/cake build:full
- node ./bin/cake build:browser
- node ./bin/cake test
- node --harmony ./bin/cake test
- node ./bin/cake test:browser
- node ./bin/cake test:integrations

46
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at maintainers@coffeescript.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -6,4 +6,9 @@
* 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. You _should,_ however, commit the updated compiled JavaScript files in `lib`.
* 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`.
* To get started, read the guides in the wiki:
* [Hacking on the CoffeeScript Compiler](https://github.com/jashkenas/coffeescript/wiki/%5BHowTo%5D-Hacking-on-the-CoffeeScript-Compiler)
* [How parsing works](https://github.com/jashkenas/coffeescript/wiki/%5BHowTo%5D-How-parsing-works)
* [Update the docs](https://github.com/jashkenas/coffeescript/wiki/%5BHowTo%5D-Update-the-docs)

View File

@@ -332,7 +332,7 @@ task 'doc:test:watch', 'watch and continually rebuild the browser-based tests',
buildAnnotatedSource = (watch = no) ->
do generateAnnotatedSource = ->
exec "node_modules/docco/bin/docco src/*.*coffee --output docs/v#{majorVersion}/annotated-source", (err) -> throw err if err
exec "cd src && ../node_modules/docco/bin/docco *.*coffee --output ../docs/v#{majorVersion}/annotated-source", (err) -> throw err if err
log 'generated', green, "annotated source in docs/v#{majorVersion}/annotated-source/"
if watch
@@ -425,12 +425,6 @@ runTests = (CoffeeScript) ->
catch err
onFail description, fn, err
global.supportsAsync = try
new Function('async () => {}')()
yes
catch
no
helpers.extend global, require './test/support/helpers'
# When all the tests have run, collect and print errors.
@@ -448,10 +442,20 @@ runTests = (CoffeeScript) ->
console.log " #{source}" if source
return
# Run every test in the `test` folder, recording failures.
files = fs.readdirSync 'test'
unless global.supportsAsync # Except for async tests, if async isnt supported.
files = files.filter (filename) -> filename isnt 'async.coffee'
# Run every test in the `test` folder, recording failures, except for files
# were skipping because the features to be tested are unsupported in the
# current Node runtime.
testFilesToSkip = []
skipUnless = (featureDetect, filenames) ->
unless (try new Function featureDetect)
testFilesToSkip = testFilesToSkip.concat filenames
skipUnless 'async () => {}', ['async.coffee', 'async_iterators.coffee']
skipUnless 'async function* generator() { yield 42; }', ['async_iterators.coffee']
skipUnless 'var a = 2 ** 2; a **= 3', ['exponentiation.coffee']
skipUnless 'var {...a} = {}', ['object_rest_spread.coffee']
skipUnless '/foo.bar/s.test("foo\tbar")', ['regex_dotall.coffee']
files = fs.readdirSync('test').filter (filename) ->
filename not in testFilesToSkip
startTime = Date.now()
for file in files when helpers.isCoffee file

62
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,62 @@
<!---
Thanks for filing an issue 😄! Before you submit, please read the following:
Search open/closed issues before submitting since someone might have asked the same thing before!
Our issues history stretches back to 2009, so the odds are good that your topic has come up.
If you have a support request or question please use Stack Overflow:
https://stackoverflow.com/questions/tagged/coffeescript
Issues on GitHub are only related to problems of the CoffeeScript compiler itself and we cannot answer
support questions here.
-->
Choose one: is this a bug report or feature request?
<!---
Provide a general summary of the issue in the title above.
For bugs, please also preface your title with “Bug:”. For feature requests, please preface your title
with “Proposal:”. Once your issue is reviewed, a maintainer will edit the title to refer to the part
of the codebase most relevant to the issue (if applicable).
If your request is that CoffeeScript support a new feature recently arrived to JavaScript, please note
that we generally only add features that have reached Stage 4 in the specification (in other words,
the syntax is finalized and the feature is approved to be part of the next ES release). You can still
open an issue, but it will likely be tagged with “[Awaiting Stage 4]” until the relevant ES feature is
approved for release. See http://coffeescript.org/#contributing
There are also a handful of JavaScript features that CoffeeScript intentionally does not support.
Please do not open issues regarding these. Theyre listed in http://coffeescript.org/#unsupported
-->
### Input Code
<!--- If you're describing a bug, please let us know which sample code reproduces your problem. -->
<!--- If you have link from http://coffeescript.org/#try or a standalone repo please include that! -->
```coffee
your (code) => here
```
### Expected Behavior
<!--- If youre describing a bug, tell us what should happen. -->
<!--- If youre suggesting a change/improvement, tell us how it should work. -->
### Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior. -->
<!--- If suggesting a change/improvement, explain the difference from current behavior. -->
### Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change. -->
### Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world. -->
### Environment
<!--- For bugs, please let us know what version of CoffeeScript youre running: `coffee -v`. -->
<!--- If you think it might be relevant, please also let us know your version of Node. -->
* CoffeeScript version:
* Node.js version:

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2017 Jeremy Ashkenas
Copyright (c) 2009-2018 Jeremy Ashkenas
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

22
PULL_REQUEST_TEMPLATE.md Normal file
View File

@@ -0,0 +1,22 @@
<!--
Before making a PR please make sure to read our contributing guidelines:
http://coffeescript.org/#contributing
For issue references: Add a comma-separated list of a
[closing word](https://help.github.com/articles/closing-issues-via-commit-messages/) followed by
the ticket number fixed by the PR. It should be underlined in the preview if done correctly.
All new features require tests. All but the most trivial bug fixes should also have new or updated tests.
Ensure that all new code you add to the compiler can be run in the minimum version of Node listed in
`package.json`. New tests can require newer Node runtimes, but you may need to ensure that such tests
only run in supported runtimes; see `Cakefile` for examples of how to filter out certain tests in
runtimes that dont support them.
Please follow the code style of the rest of the CoffeeScript codebase. Write comments in complete
sentences using Markdown, as the comments become the [annotated source](http://coffeescript.org/#annotated-source).
For tests proving a bug is fixed, please mention the issue number in the test description (see examples
in the codebase).
Describe your changes below in as much detail as possible.
-->

View File

@@ -1,27 +1,27 @@
@@@@@@@ @@@@ @@@@@
{ @@@@@@@@@@ @@@ @@@
} } { @@@@ @@ @@@ @@@
{ { } } @@@@ @@@@@@@ @@@ @@@ @@@@@@ @@@@@@
} }{ { @@@@ @@@ @@ @@@@@ @@@@@@ @@@ @@ @@@@ @@
{ }{ } } @@@@ @@@@ @@ @@@ @@@ @@@ @@@ @@@ @@@
{ }{ }{ { } @@@@ @@@@ @@ @@@ @@@ @@@@@@@@ @@@@@@@@
{ { } { } { } } @@@@@ @@@@ @@ @@@ @@@ @@@ @@@
{ } { } { } @@@@@@@@@@ @@@@@@@@ @@@ @@@ @@@@@@@@ @@@@@@@@
@@@@@@ { } { } @@@@@@@ @@@@@ @@@ @@@ @@@@@ @@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@ @@@
@@ @@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@ @@@ @@@
@@ @@@@@@@@@@@@@@@@@@@@@@@@ @@@@ @@ @@@ @@@@
@@@ @@@@@@@@@@@@@@@@@@@@@ @@@@ @@@ @@ @@@@
@@@ @@@@@@@@@@@@@@@@@@ @@@@@ @@@@@ @@ @@ @@@ @@@@@@@ @@@@@
@@@ @@@@@@@@@@@@@@@@ @@@@@ @@@ @@@ @@@@@@@@ @@@@ @@@@ @@@@@@@
@@@@@@@@@@@@@@ @@@@@ @@@ @@@@ @@@@ @@@ @@@ @@@
@@@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
@@@ @@@@ @@@ @@@@ @@@@ @@@ @@@@ @@@@
@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
@@@@@@@@@ @@@@@@ @@@@ @@@@ @@@@@@@@@ @@@@
@@@ @@@@
@@@
@@@
@@@@@@@ @@@@ @@@@@
@@@@@@@@@@ @@@ @@@ {
@@@@ @@ @@@ @@@ } } {
@@@@ @@@@@@@ @@@ @@@ @@@@@@ @@@@@@ { { } }
@@@@ @@@ @@ @@@@@ @@@@@@ @@@ @@ @@@@ @@ } }{ {
@@@@ @@@@ @@ @@@ @@@ @@@ @@@ @@@ @@@ { }{ } }
@@@@ @@@@ @@ @@@ @@@ @@@@@@@@ @@@@@@@@ { }{ }{ { }
@@@@@ @@@@ @@ @@@ @@@ @@@ @@@ { { } { } { } }
@@@@@@@@@@ @@@@@@@@ @@@ @@@ @@@@@@@@ @@@@@@@@ { } { } { }
@@@@@ @@@ @@@ @@@@@ @@@@@ @@@@@@ { } { } @@@@@@@
@@@ @@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@ @@@ @@@ @@ @@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@ @@ @@@ @@@@ @@ @@@@@@@@@@@@@@@@@@@@@@@@
@@@@ @@@ @@ @@@@ @@@ @@@@@@@@@@@@@@@@@@@@@
@@@@@ @@@@@ @@ @@ @@@ @@@@@@@ @@@@@ @@@ @@@@@@@@@@@@@@@@@@
@@@@@ @@@ @@@ @@@@@@@@ @@@@ @@@@ @@@@@@@ @@@ @@@@@@@@@@@@@@@@
@@@@@ @@@ @@@@ @@@@ @@@ @@@ @@@ @@@@@@@@@@@@@@
@@@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
@@@ @@@@ @@@ @@@@ @@@@ @@@ @@@@ @@@@
@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
@@@@@@@@@ @@@@@@ @@@@ @@@@ @@@@@@@@@ @@@@
@@@ @@@@
@@@
@@@
CoffeeScript is a little language that compiles into JavaScript.
@@ -30,11 +30,13 @@ CoffeeScript is a little language that compiles into JavaScript.
Once you have Node.js installed:
```shell
# Install locally for a project:
npm install --save-dev coffeescript
# Install globally to execute .coffee files anywhere:
npm install --global coffeescript
```
Leave off the `--global` if you dont wish to install globally.
## Getting Started
Execute a script:

29
appveyor.yml Normal file
View File

@@ -0,0 +1,29 @@
environment:
matrix:
- nodejs_version: '6'
- nodejs_version: '8'
- nodejs_version: '10'
- nodejs_version: '' # Installs latest.
install:
- ps: Install-Product node $env:nodejs_version
- node --version
- npm --version
- npm install
cache:
- node_modules
test_script:
- node ./bin/cake build:except-parser
- node ./bin/cake build:parser
- node ./bin/cake build:full
- node ./bin/cake build:browser
- node ./bin/cake test
- node --harmony ./bin/cake test
- node ./bin/cake test:browser
- node ./bin/cake test:integrations
build: off
version: "{build}"

View File

@@ -1,5 +1,12 @@
#!/usr/bin/env node
try {
new Function('var {a} = {a: 1}')();
} catch (error) {
console.error('Your JavaScript runtime does not support some features used by the cake command. Please use Node 6 or later.');
process.exit(1);
}
var path = require('path');
var fs = require('fs');

View File

@@ -1,5 +1,12 @@
#!/usr/bin/env node
try {
new Function('var {a} = {a: 1}')();
} catch (error) {
console.error('Your JavaScript runtime does not support some features used by the coffee command. Please use Node 6 or later.');
process.exit(1);
}
var path = require('path');
var fs = require('fs');

View File

@@ -480,7 +480,9 @@ is run via the CLI or Node API.</p>
</div>
<div class="content"><div class='highlight'><pre> transpiler = options.transpile.transpile
<span class="hljs-keyword">delete</span> options.transpile.transpile</pre></div></div>
<span class="hljs-keyword">delete</span> options.transpile.transpile
transpilerOptions = Object.assign {}, options.transpile</pre></div></div>
</li>
@@ -497,9 +499,9 @@ and it will return an <em>updated</em> v3 source map object in its output.</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> v3SourceMap <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> options.transpile.inputSourceMap?
options.transpile.inputSourceMap = v3SourceMap
transpilerOutput = transpiler js, options.transpile
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> v3SourceMap <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> transpilerOptions.inputSourceMap?
transpilerOptions.inputSourceMap = v3SourceMap
transpilerOutput = transpiler js, transpilerOptions
js = transpilerOutput.code
<span class="hljs-keyword">if</span> v3SourceMap <span class="hljs-keyword">and</span> transpilerOutput.map
v3SourceMap = transpilerOutput.map

View File

@@ -749,7 +749,7 @@ 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">writeJs</span> = <span class="hljs-params">(base, sourcePath, js, jsPath, generatedSourceMap = <span class="hljs-literal">null</span>)</span> -&gt;</span>
sourceMapPath = outputPath sourcePath, base, <span class="hljs-string">".js.map"</span>
sourceMapPath = <span class="hljs-string">"<span class="hljs-subst">#{jsPath}</span>.map"</span>
jsDir = path.dirname jsPath
<span class="hljs-function"> <span class="hljs-title">compile</span> = -&gt;</span>
<span class="hljs-keyword">if</span> opts.compile

File diff suppressed because it is too large Load Diff

View File

@@ -461,13 +461,17 @@ what CoffeeScript would normally interpret as calls to functions named
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'PROPERTY'</span> <span class="hljs-keyword">and</span> prev
<span class="hljs-keyword">if</span> prev.spaced <span class="hljs-keyword">and</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> CALLABLE <span class="hljs-keyword">and</span> <span class="hljs-regexp">/^[gs]et$/</span>.test(prev[<span class="hljs-number">1</span>]) <span class="hljs-keyword">and</span> @tokens[@tokens.length - <span class="hljs-number">2</span>][<span class="hljs-number">0</span>] <span class="hljs-keyword">isnt</span> <span class="hljs-string">'.'</span>
@error <span class="hljs-string">"'<span class="hljs-subst">#{prev[<span class="hljs-number">1</span>]}</span>' cannot be used as a keyword, or as a function call without parentheses"</span>, prev[<span class="hljs-number">2</span>]
<span class="hljs-keyword">else</span>
<span class="hljs-keyword">if</span> prev.spaced <span class="hljs-keyword">and</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> CALLABLE <span class="hljs-keyword">and</span> <span class="hljs-regexp">/^[gs]et$/</span>.test(prev[<span class="hljs-number">1</span>]) <span class="hljs-keyword">and</span>
@tokens.length &gt; <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> @tokens[@tokens.length - <span class="hljs-number">2</span>][<span class="hljs-number">0</span>] <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">'.'</span>, <span class="hljs-string">'?.'</span>, <span class="hljs-string">'@'</span>]
@error <span class="hljs-string">"'<span class="hljs-subst">#{prev[<span class="hljs-number">1</span>]}</span>' cannot be used as a keyword, or as a function call
without parentheses"</span>, prev[<span class="hljs-number">2</span>]
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @tokens.length &gt; <span class="hljs-number">2</span>
prevprev = @tokens[@tokens.length - <span class="hljs-number">2</span>]
<span class="hljs-keyword">if</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'@'</span>, <span class="hljs-string">'THIS'</span>] <span class="hljs-keyword">and</span> prevprev <span class="hljs-keyword">and</span> prevprev.spaced <span class="hljs-keyword">and</span> <span class="hljs-regexp">/^[gs]et$/</span>.test(prevprev[<span class="hljs-number">1</span>]) <span class="hljs-keyword">and</span>
@tokens[@tokens.length - <span class="hljs-number">3</span>][<span class="hljs-number">0</span>] <span class="hljs-keyword">isnt</span> <span class="hljs-string">'.'</span>
@error <span class="hljs-string">"'<span class="hljs-subst">#{prevprev[<span class="hljs-number">1</span>]}</span>' cannot be used as a keyword, or as a function call without parentheses"</span>, prevprev[<span class="hljs-number">2</span>]
<span class="hljs-keyword">if</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'@'</span>, <span class="hljs-string">'THIS'</span>] <span class="hljs-keyword">and</span> prevprev <span class="hljs-keyword">and</span> prevprev.spaced <span class="hljs-keyword">and</span>
<span class="hljs-regexp">/^[gs]et$/</span>.test(prevprev[<span class="hljs-number">1</span>]) <span class="hljs-keyword">and</span>
@tokens[@tokens.length - <span class="hljs-number">3</span>][<span class="hljs-number">0</span>] <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">'.'</span>, <span class="hljs-string">'?.'</span>, <span class="hljs-string">'@'</span>]
@error <span class="hljs-string">"'<span class="hljs-subst">#{prevprev[<span class="hljs-number">1</span>]}</span>' cannot be used as a keyword, or as a
function call without parentheses"</span>, prevprev[<span class="hljs-number">2</span>]
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'IDENTIFIER'</span> <span class="hljs-keyword">and</span> id <span class="hljs-keyword">in</span> RESERVED
@error <span class="hljs-string">"reserved word '<span class="hljs-subst">#{id}</span>'"</span>, length: id.length
@@ -915,9 +919,11 @@ can close multiple indents, so we need to know how far in we happen to be.</p>
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> match = MULTI_DENT.exec chunk
indent = match[<span class="hljs-number">0</span>]
@seenFor = <span class="hljs-literal">no</span>
@seenImport = <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> @importSpecifierList
@seenExport = <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> @exportSpecifierList
prev = @prev()
backslash = prev?[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'\\'</span>
@seenFor = <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> backslash <span class="hljs-keyword">and</span> @seenFor
@seenImport = <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> (backslash <span class="hljs-keyword">and</span> @seenImport) <span class="hljs-keyword">or</span> @importSpecifierList
@seenExport = <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> (backslash <span class="hljs-keyword">and</span> @seenExport) <span class="hljs-keyword">or</span> @exportSpecifierList
size = indent.length - <span class="hljs-number">1</span> - indent.lastIndexOf <span class="hljs-string">'\n'</span>
noNewlines = @unfinished()
@@ -938,7 +944,7 @@ can close multiple indents, so we need to know how far in we happen to be.</p>
<span class="hljs-keyword">if</span> size &gt; @indent
<span class="hljs-keyword">if</span> noNewlines
@indebt = size - @indent
@indebt = size - @indent <span class="hljs-keyword">unless</span> backslash
@suppressNewlines()
<span class="hljs-keyword">return</span> indent.length
<span class="hljs-keyword">unless</span> @tokens.length
@@ -1121,7 +1127,7 @@ comments that trail it.</p>
<div class="content"><div class='highlight'><pre> prevChar = <span class="hljs-keyword">if</span> @tokens.length &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">then</span> @tokens[@tokens.length - <span class="hljs-number">1</span>][<span class="hljs-number">0</span>] <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>
<span class="hljs-keyword">if</span> firstChar <span class="hljs-keyword">is</span> <span class="hljs-string">'&lt;'</span>
match = CSX_IDENTIFIER.exec @chunk[<span class="hljs-number">1.</span>..]
match = CSX_IDENTIFIER.exec(@chunk[<span class="hljs-number">1.</span>..]) <span class="hljs-keyword">or</span> CSX_FRAGMENT_IDENTIFIER.exec(@chunk[<span class="hljs-number">1.</span>..])
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> match <span class="hljs-keyword">and</span> (
@csxDepth &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">or</span></pre></div></div>
@@ -1187,8 +1193,8 @@ comments that trail it.</p>
@matchWithInterpolations INSIDE_CSX, <span class="hljs-string">'&gt;'</span>, <span class="hljs-string">'&lt;/'</span>, CSX_INTERPOLATION
@mergeInterpolationTokens tokens, {delimiter: <span class="hljs-string">'"'</span>}, <span class="hljs-function"><span class="hljs-params">(value, i)</span> =&gt;</span>
@formatString value, delimiter: <span class="hljs-string">'&gt;'</span>
match = CSX_IDENTIFIER.exec @chunk[end...]
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> match <span class="hljs-keyword">or</span> match[<span class="hljs-number">0</span>] <span class="hljs-keyword">isnt</span> csxTag.name
match = CSX_IDENTIFIER.exec(@chunk[end...]) <span class="hljs-keyword">or</span> CSX_FRAGMENT_IDENTIFIER.exec(@chunk[end...])
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> match <span class="hljs-keyword">or</span> match[<span class="hljs-number">1</span>] <span class="hljs-keyword">isnt</span> csxTag.name
@error <span class="hljs-string">"expected corresponding CSX closing tag for <span class="hljs-subst">#{csxTag.name}</span>"</span>,
csxTag.origin[<span class="hljs-number">2</span>]
afterTag = end + csxTag.name.length
@@ -1641,8 +1647,8 @@ of <code>&#39;NEOSTRING&#39;</code>s are converted using <code>fn</code> and tur
<a class="pilcrow" href="#section-58">&#182;</a>
</div>
<p>This is an interpolated string, not a CSX tag; and for whatever
reason <code>`a${/*test*/}b` </code> is invalid JS. So compile to
<code>`a${/*test*/&#39;&#39;}b` </code> instead.</p>
reason <code>`a${/*test*/}b`</code> is invalid JS. So compile to
<code>`a${/*test*/&#39;&#39;}b`</code> instead.</p>
</div>
@@ -2458,6 +2464,23 @@ be used as identifiers or properties.</p>
CSX_IDENTIFIER = <span class="hljs-regexp">/// ^
(?![\d&lt;]) <span class="hljs-comment"># Must not start with `&lt;`.</span>
( (?: (?!\s)[\.\-$\w\x7f-\uffff] )+ ) <span class="hljs-comment"># Like `IDENTIFIER`, but includes `-`s and `.`s.</span>
///</span></pre></div></div>
</li>
<li id="section-98">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-98">&#182;</a>
</div>
<p>Fragment: &lt;&gt;&lt;/&gt;</p>
</div>
<div class="content"><div class='highlight'><pre>CSX_FRAGMENT_IDENTIFIER = <span class="hljs-regexp">/// ^
()&gt; <span class="hljs-comment"># Ends immediately with `&gt;`.</span>
///</span>
CSX_ATTRIBUTE = <span class="hljs-regexp">/// ^
@@ -2497,11 +2520,11 @@ HERE_JSTOKEN = <span class="hljs-regexp">///^ ``` ((?: [^`\\] | \\[\s\S] | `
</li>
<li id="section-98">
<li id="section-99">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-98">&#182;</a>
<a class="pilcrow" href="#section-99">&#182;</a>
</div>
<p>String-matching-regexes.</p>
@@ -2535,11 +2558,11 @@ HEREDOC_INDENT = <span class="hljs-regexp">/\n+([^\n\S]*)(?=\S)/g</span></pr
</li>
<li id="section-99">
<li id="section-100">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-99">&#182;</a>
<a class="pilcrow" href="#section-100">&#182;</a>
</div>
<p>Regex-matching-regexes.</p>
@@ -2556,9 +2579,88 @@ HEREDOC_INDENT = <span class="hljs-regexp">/\n+([^\n\S]*)(?=\S)/g</span></pr
///</span>
REGEX_FLAGS = <span class="hljs-regexp">/^\w*/</span>
VALID_FLAGS = <span class="hljs-regexp">/^(?!.*(.).*\1)[imguy]*$/</span>
VALID_FLAGS = <span class="hljs-regexp">/^(?!.*(.).*\1)[gimsuy]*$/</span>
HEREGEX = <span class="hljs-regexp">/// ^(?: [^\\/<span class="hljs-comment">#] | \\[\s\S] | /(?!//) | \#(?!\{) )* ///</span>
HEREGEX = <span class="hljs-regexp">/// ^
(?:
</span></pre></div></div>
</li>
<li id="section-101">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-101">&#182;</a>
</div>
<p>Match any character, except those that need special handling below.</p>
</div>
<div class="content"><div class='highlight'><pre> [^\\/<span class="hljs-comment">#\s]</span></pre></div></div>
</li>
<li id="section-102">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-102">&#182;</a>
</div>
<p>Match <code>\</code> followed by any character.</p>
</div>
<div class="content"><div class='highlight'><pre> | \\[\s\S]</pre></div></div>
</li>
<li id="section-103">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-103">&#182;</a>
</div>
<p>Match any <code>/</code> except <code>///</code>.</p>
</div>
<div class="content"><div class='highlight'><pre> | <span class="hljs-regexp">/(?!/</span>/)</pre></div></div>
</li>
<li id="section-104">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-104">&#182;</a>
</div>
<p>Match <code>#</code> which is not part of interpolation, e.g. <code>#{}</code>.</p>
</div>
<div class="content"><div class='highlight'><pre> | \<span class="hljs-comment">#(?!\{)</span></pre></div></div>
</li>
<li id="section-105">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-105">&#182;</a>
</div>
<p>Comments consume everything until the end of the line, including <code>///</code>.</p>
</div>
<div class="content"><div class='highlight'><pre> | \s+(?:<span class="hljs-comment">#(?!\{).*)?</span>
)*
<span class="hljs-regexp">///
HEREGEX_OMIT = ///</span>
((?:\\\\)+) <span class="hljs-comment"># Consume (and preserve) an even number of backslashes.</span>
@@ -2573,11 +2675,11 @@ POSSIBLY_DIVISION = <span class="hljs-regexp">/// ^ /=?\s ///</span></pre></di
</li>
<li id="section-100">
<li id="section-106">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-100">&#182;</a>
<a class="pilcrow" href="#section-106">&#182;</a>
</div>
<p>Other regexes.</p>
@@ -2620,11 +2722,11 @@ TRAILING_SPACES = <span class="hljs-regexp">/\s+$/</span></pre></div></div>
</li>
<li id="section-101">
<li id="section-107">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-101">&#182;</a>
<a class="pilcrow" href="#section-107">&#182;</a>
</div>
<p>Compound assignment tokens.</p>
@@ -2638,11 +2740,11 @@ TRAILING_SPACES = <span class="hljs-regexp">/\s+$/</span></pre></div></div>
</li>
<li id="section-102">
<li id="section-108">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-102">&#182;</a>
<a class="pilcrow" href="#section-108">&#182;</a>
</div>
<p>Unary tokens.</p>
@@ -2655,11 +2757,11 @@ UNARY_MATH = [<span class="hljs-string">'!'</span>, <span class="hljs-string">'~
</li>
<li id="section-103">
<li id="section-109">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-103">&#182;</a>
<a class="pilcrow" href="#section-109">&#182;</a>
</div>
<p>Bit-shifting tokens.</p>
@@ -2670,11 +2772,11 @@ UNARY_MATH = [<span class="hljs-string">'!'</span>, <span class="hljs-string">'~
</li>
<li id="section-104">
<li id="section-110">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-104">&#182;</a>
<a class="pilcrow" href="#section-110">&#182;</a>
</div>
<p>Comparison tokens.</p>
@@ -2685,11 +2787,11 @@ UNARY_MATH = [<span class="hljs-string">'!'</span>, <span class="hljs-string">'~
</li>
<li id="section-105">
<li id="section-111">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-105">&#182;</a>
<a class="pilcrow" href="#section-111">&#182;</a>
</div>
<p>Mathematical tokens.</p>
@@ -2700,11 +2802,11 @@ UNARY_MATH = [<span class="hljs-string">'!'</span>, <span class="hljs-string">'~
</li>
<li id="section-106">
<li id="section-112">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-106">&#182;</a>
<a class="pilcrow" href="#section-112">&#182;</a>
</div>
<p>Relational tokens that are negatable with <code>not</code> prefix.</p>
@@ -2715,11 +2817,11 @@ UNARY_MATH = [<span class="hljs-string">'!'</span>, <span class="hljs-string">'~
</li>
<li id="section-107">
<li id="section-113">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-107">&#182;</a>
<a class="pilcrow" href="#section-113">&#182;</a>
</div>
<p>Boolean tokens.</p>
@@ -2730,11 +2832,11 @@ UNARY_MATH = [<span class="hljs-string">'!'</span>, <span class="hljs-string">'~
</li>
<li id="section-108">
<li id="section-114">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-108">&#182;</a>
<a class="pilcrow" href="#section-114">&#182;</a>
</div>
<p>Tokens which could legitimately be invoked or indexed. An opening
parentheses or bracket following these tokens will be recorded as the start
@@ -2751,11 +2853,11 @@ INDEXABLE = CALLABLE.concat [
</li>
<li id="section-109">
<li id="section-115">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-109">&#182;</a>
<a class="pilcrow" href="#section-115">&#182;</a>
</div>
<p>Tokens which can be the left-hand side of a less-than comparison, i.e. <code>a&lt;b</code>.</p>
@@ -2766,11 +2868,11 @@ INDEXABLE = CALLABLE.concat [
</li>
<li id="section-110">
<li id="section-116">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-110">&#182;</a>
<a class="pilcrow" href="#section-116">&#182;</a>
</div>
<p>Tokens which a regular expression will never immediately follow (except spaced
CALLABLEs in some cases), but which a division operator can.</p>
@@ -2783,11 +2885,11 @@ CALLABLEs in some cases), but which a division operator can.</p>
</li>
<li id="section-111">
<li id="section-117">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-111">&#182;</a>
<a class="pilcrow" href="#section-117">&#182;</a>
</div>
<p>Tokens that, when immediately preceding a <code>WHEN</code>, indicate that the <code>WHEN</code>
occurs at the start of a line. We disambiguate these from trailing whens to
@@ -2800,11 +2902,11 @@ avoid an ambiguity in the grammar.</p>
</li>
<li id="section-112">
<li id="section-118">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-112">&#182;</a>
<a class="pilcrow" href="#section-118">&#182;</a>
</div>
<p>Additional indent in front of these is ignored.</p>
@@ -2815,11 +2917,11 @@ avoid an ambiguity in the grammar.</p>
</li>
<li id="section-113">
<li id="section-119">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-113">&#182;</a>
<a class="pilcrow" href="#section-119">&#182;</a>
</div>
<p>Tokens that, when appearing at the end of a line, suppress a following TERMINATOR/INDENT token</p>
@@ -2827,7 +2929,7 @@ avoid an ambiguity in the grammar.</p>
<div class="content"><div class='highlight'><pre>UNFINISHED = [<span class="hljs-string">'\\'</span>, <span class="hljs-string">'.'</span>, <span class="hljs-string">'?.'</span>, <span class="hljs-string">'?::'</span>, <span class="hljs-string">'UNARY'</span>, <span class="hljs-string">'MATH'</span>, <span class="hljs-string">'UNARY_MATH'</span>, <span class="hljs-string">'+'</span>, <span class="hljs-string">'-'</span>,
<span class="hljs-string">'**'</span>, <span class="hljs-string">'SHIFT'</span>, <span class="hljs-string">'RELATION'</span>, <span class="hljs-string">'COMPARE'</span>, <span class="hljs-string">'&amp;'</span>, <span class="hljs-string">'^'</span>, <span class="hljs-string">'|'</span>, <span class="hljs-string">'&amp;&amp;'</span>, <span class="hljs-string">'||'</span>,
<span class="hljs-string">'BIN?'</span>, <span class="hljs-string">'EXTENDS'</span>, <span class="hljs-string">'DEFAULT'</span>]</pre></div></div>
<span class="hljs-string">'BIN?'</span>, <span class="hljs-string">'EXTENDS'</span>]</pre></div></div>
</li>

File diff suppressed because it is too large Load Diff

0
docs/v2/annotated-source/public/fonts/roboto-black.eot Normal file → Executable file
View File

0
docs/v2/annotated-source/public/fonts/roboto-black.ttf Normal file → Executable file
View File

View File

View File

@@ -224,11 +224,17 @@ Unwrap that too.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-7">&#182;</a>
</div>
<p>Collect referenced variable names just like in <code>CoffeeScript.compile</code>.</p>
<p>Filter out tokens generated just to hold comments.</p>
</div>
<div class="content"><div class='highlight'><pre> referencedVars = (token[<span class="hljs-number">1</span>] <span class="hljs-keyword">for</span> token <span class="hljs-keyword">in</span> tokens <span class="hljs-keyword">when</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'IDENTIFIER'</span>)</pre></div></div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tokens.length &gt;= <span class="hljs-number">2</span> <span class="hljs-keyword">and</span> tokens[<span class="hljs-number">0</span>].generated <span class="hljs-keyword">and</span>
tokens[<span class="hljs-number">0</span>].comments?.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> tokens[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">''</span> <span class="hljs-keyword">and</span>
tokens[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span>
tokens = tokens[<span class="hljs-number">2.</span>..]
<span class="hljs-keyword">if</span> tokens.length &gt;= <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> tokens[tokens.length - <span class="hljs-number">1</span>].generated <span class="hljs-keyword">and</span>
tokens[tokens.length - <span class="hljs-number">1</span>].comments?.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> tokens[tokens.length - <span class="hljs-number">1</span>][<span class="hljs-number">1</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">''</span>
tokens.pop()</pre></div></div>
</li>
@@ -239,11 +245,11 @@ Unwrap that too.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-8">&#182;</a>
</div>
<p>Generate the AST of the tokens.</p>
<p>Collect referenced variable names just like in <code>CoffeeScript.compile</code>.</p>
</div>
<div class="content"><div class='highlight'><pre> ast = CoffeeScript.nodes tokens</pre></div></div>
<div class="content"><div class='highlight'><pre> referencedVars = (token[<span class="hljs-number">1</span>] <span class="hljs-keyword">for</span> token <span class="hljs-keyword">in</span> tokens <span class="hljs-keyword">when</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'IDENTIFIER'</span>)</pre></div></div>
</li>
@@ -254,11 +260,11 @@ Unwrap that too.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-9">&#182;</a>
</div>
<p>Add assignment to <code>__</code> variable to force the input to be an expression.</p>
<p>Generate the AST of the tokens.</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>]</pre></div></div>
<div class="content"><div class='highlight'><pre> ast = CoffeeScript.nodes tokens</pre></div></div>
</li>
@@ -269,6 +275,21 @@ Unwrap that too.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-10">&#182;</a>
</div>
<p>Add assignment to <code>__</code> variable to force the input to be an expression.</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>]</pre></div></div>
</li>
<li id="section-11">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-11">&#182;</a>
</div>
<p>Wrap the expression in a closure to support top-level <code>await</code>.</p>
</div>
@@ -279,11 +300,11 @@ Unwrap that too.</p>
</li>
<li id="section-11">
<li id="section-12">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-11">&#182;</a>
<a class="pilcrow" href="#section-12">&#182;</a>
</div>
<p>Invoke the wrapping closure.</p>
@@ -297,11 +318,11 @@ Unwrap that too.</p>
</li>
<li id="section-12">
<li id="section-13">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-12">&#182;</a>
<a class="pilcrow" href="#section-13">&#182;</a>
</div>
<p>Strip <code>&quot;use strict&quot;</code>, to avoid an exception on assigning to
undeclared variable <code>__</code>.</p>
@@ -314,11 +335,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-13">
<li id="section-14">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-13">&#182;</a>
<a class="pilcrow" href="#section-14">&#182;</a>
</div>
<p>Await an async result, if necessary.</p>
@@ -335,11 +356,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-14">
<li id="section-15">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-14">&#182;</a>
<a class="pilcrow" href="#section-15">&#182;</a>
</div>
<p>ASTs <code>compile</code> does not add source code information to syntax errors.</p>
@@ -360,11 +381,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-15">
<li id="section-16">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-15">&#182;</a>
<a class="pilcrow" href="#section-16">&#182;</a>
</div>
<p>Node 0.11.12 changed API, prompt is now _prompt.</p>
@@ -381,11 +402,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-16">
<li id="section-17">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-16">&#182;</a>
<a class="pilcrow" href="#section-17">&#182;</a>
</div>
<p>Proxy nodes line listener</p>
@@ -406,11 +427,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-17">
<li id="section-18">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-17">&#182;</a>
<a class="pilcrow" href="#section-18">&#182;</a>
</div>
<p>Handle Ctrl-v</p>
@@ -423,11 +444,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-18">
<li id="section-19">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-18">&#182;</a>
<a class="pilcrow" href="#section-19">&#182;</a>
</div>
<p>allow arbitrarily switching between modes any time before multiple lines are entered</p>
@@ -442,11 +463,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-19">
<li id="section-20">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-19">&#182;</a>
<a class="pilcrow" href="#section-20">&#182;</a>
</div>
<p>no-op unless the current line is empty</p>
@@ -457,11 +478,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-20">
<li id="section-21">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-20">&#182;</a>
<a class="pilcrow" href="#section-21">&#182;</a>
</div>
<p>eval, print, loop</p>
@@ -476,11 +497,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-21">
<li id="section-22">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-21">&#182;</a>
<a class="pilcrow" href="#section-22">&#182;</a>
</div>
<p>XXX: multiline hack</p>
@@ -498,11 +519,11 @@ undeclared variable <code>__</code>.</p>
</li>
<li id="section-22">
<li id="section-23">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-22">&#182;</a>
<a class="pilcrow" href="#section-23">&#182;</a>
</div>
<p>Store and load command history from a file</p>
@@ -515,11 +536,11 @@ undeclared variable <code>__</code>.</p>
</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>Get file info and at most maxSize of command history</p>
@@ -531,11 +552,11 @@ undeclared variable <code>__</code>.</p>
</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>Read last <code>size</code> bytes from the file</p>
@@ -549,11 +570,11 @@ undeclared variable <code>__</code>.</p>
</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>Set the history on the interpreter</p>
@@ -564,11 +585,11 @@ undeclared variable <code>__</code>.</p>
</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>If the history file was truncated we should pop off a potential partial line</p>
@@ -579,11 +600,11 @@ undeclared variable <code>__</code>.</p>
</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>Shift off the final blank newline</p>
@@ -601,11 +622,11 @@ undeclared variable <code>__</code>.</p>
</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>Save the latest command in the file</p>
@@ -617,11 +638,11 @@ undeclared variable <code>__</code>.</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>XXX: The SIGINT event from REPLServer is undocumented, so this is a bit fragile</p>
@@ -633,11 +654,11 @@ undeclared variable <code>__</code>.</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>Add a command to show the history stack</p>
@@ -654,11 +675,11 @@ undeclared variable <code>__</code>.</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>Node 0.11 changed API, a command such as .help is now stored as help</p>
@@ -697,11 +718,11 @@ undeclared variable <code>__</code>.</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>Since the REPL compilation path is unique (in <code>eval</code> above), we need
another way to get the <code>options</code> object attached to a module so that
@@ -725,11 +746,11 @@ the REPL, the only applicable option is <code>transpile</code>.</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>Adapt help inherited from the node REPL</p>

View File

@@ -676,7 +676,21 @@ class declaration or if-conditionals).</p>
endImplicitObject()
<span class="hljs-keyword">else</span>
stack.pop()
start = stack.pop()</pre></div></div>
start = stack.pop()
<span class="hljs-function">
<span class="hljs-title">inControlFlow</span> = =&gt;</span>
seenFor = @findTagsBackwards(i, [<span class="hljs-string">'FOR'</span>]) <span class="hljs-keyword">and</span> @findTagsBackwards(i, [<span class="hljs-string">'FORIN'</span>, <span class="hljs-string">'FOROF'</span>, <span class="hljs-string">'FORFROM'</span>])
controlFlow = seenFor <span class="hljs-keyword">or</span> @findTagsBackwards i, [<span class="hljs-string">'WHILE'</span>, <span class="hljs-string">'UNTIL'</span>, <span class="hljs-string">'LOOP'</span>, <span class="hljs-string">'LEADING_WHEN'</span>]
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> controlFlow
isFunc = <span class="hljs-literal">no</span>
tagCurrentLine = token[<span class="hljs-number">2</span>].first_line
@detectEnd i,
<span class="hljs-function"><span class="hljs-params">(token, i)</span> -&gt;</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> LINEBREAKS
(token, i) -&gt;
[prevTag, ,{first_line}] = tokens[i - <span class="hljs-number">1</span>] || []
isFunc = tagCurrentLine <span class="hljs-keyword">is</span> first_line <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">in</span> [<span class="hljs-string">'-&gt;'</span>, <span class="hljs-string">'=&gt;'</span>]
returnOnNegativeLevel: <span class="hljs-literal">yes</span>
isFunc</pre></div></div>
</li>
@@ -698,7 +712,8 @@ Added support for spread dots on the left side: f …a</p>
(nextTag <span class="hljs-keyword">in</span> IMPLICIT_CALL <span class="hljs-keyword">or</span>
(nextTag <span class="hljs-keyword">is</span> <span class="hljs-string">'...'</span> <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">2</span>) <span class="hljs-keyword">in</span> IMPLICIT_CALL <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @findTagsBackwards(i, [<span class="hljs-string">'INDEX_START'</span>, <span class="hljs-string">'['</span>])) <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> nextToken.spaced <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> nextToken.newLine)
<span class="hljs-keyword">not</span> nextToken.spaced <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> nextToken.newLine) <span class="hljs-keyword">and</span>
<span class="hljs-keyword">not</span> inControlFlow()
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>
@@ -835,7 +850,9 @@ like e.g.:</p>
stackItem[<span class="hljs-number">2</span>].sameLine = <span class="hljs-literal">no</span> <span class="hljs-keyword">if</span> isImplicitObject stackItem
newLine = prevTag <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">or</span> prevToken.newLine
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> IMPLICIT_END <span class="hljs-keyword">or</span> tag <span class="hljs-keyword">in</span> CALL_CLOSERS <span class="hljs-keyword">and</span> newLine
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> IMPLICIT_END <span class="hljs-keyword">or</span>
(tag <span class="hljs-keyword">in</span> CALL_CLOSERS <span class="hljs-keyword">and</span> newLine) <span class="hljs-keyword">or</span>
(tag <span class="hljs-keyword">in</span> [<span class="hljs-string">'..'</span>, <span class="hljs-string">'...'</span>] <span class="hljs-keyword">and</span> @findTagsBackwards(i, [<span class="hljs-string">"INDEX_START"</span>]))
<span class="hljs-keyword">while</span> inImplicit()
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()</pre></div></div>
@@ -852,7 +869,8 @@ like e.g.:</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> inImplicitCall() <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">','</span>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> inImplicitCall() <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">','</span> <span class="hljs-keyword">or</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">'TERMINATOR'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> nextTag?)
endImplicitCall()</pre></div></div>
</li>
@@ -916,7 +934,7 @@ e = <span class="hljs-number">2</span>
</code></pre>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @looksObjectish(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">and</span> inImplicitObject() <span class="hljs-keyword">and</span>
<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> (@tag(i + <span class="hljs-number">2</span>) <span class="hljs-keyword">in</span> [<span class="hljs-string">'FOROF'</span>, <span class="hljs-string">'FORIN'</span>]) <span class="hljs-keyword">and</span>
(nextTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> @looksObjectish(i + <span class="hljs-number">2</span>))</pre></div></div>
</li>
@@ -1245,20 +1263,101 @@ blocks are added.</p>
<div class="content"><div class='highlight'><pre> normalizeLines: <span class="hljs-function">-&gt;</span>
starter = indent = outdent = <span class="hljs-literal">null</span>
leading_switch_when = <span class="hljs-literal">null</span>
leading_if_then = <span class="hljs-literal">null</span></pre></div></div>
</li>
<li id="section-50">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-50">&#182;</a>
</div>
<p>Count <code>THEN</code> tags</p>
</div>
<div class="content"><div class='highlight'><pre> ifThens = []
<span class="hljs-function">
<span class="hljs-title">condition</span> = <span class="hljs-params">(token, i)</span> -&gt;</span>
token[<span class="hljs-number">1</span>] <span class="hljs-keyword">isnt</span> <span class="hljs-string">';'</span> <span class="hljs-keyword">and</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> SINGLE_CLOSERS <span class="hljs-keyword">and</span>
<span class="hljs-keyword">not</span> (token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> EXPRESSION_CLOSE) <span class="hljs-keyword">and</span>
<span class="hljs-keyword">not</span> (token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> starter <span class="hljs-keyword">isnt</span> <span class="hljs-string">'THEN'</span>) <span class="hljs-keyword">and</span>
<span class="hljs-keyword">not</span> (token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span>
(starter <span class="hljs-keyword">isnt</span> <span class="hljs-string">'THEN'</span> <span class="hljs-keyword">or</span> (leading_if_then <span class="hljs-keyword">or</span> leading_switch_when))) <span class="hljs-keyword">and</span>
<span class="hljs-keyword">not</span> (token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'CATCH'</span>, <span class="hljs-string">'FINALLY'</span>] <span class="hljs-keyword">and</span> starter <span class="hljs-keyword">in</span> [<span class="hljs-string">'-&gt;'</span>, <span class="hljs-string">'=&gt;'</span>]) <span class="hljs-keyword">or</span>
token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> CALL_CLOSERS <span class="hljs-keyword">and</span>
(@tokens[i - <span class="hljs-number">1</span>].newLine <span class="hljs-keyword">or</span> @tokens[i - <span class="hljs-number">1</span>][<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span>)
<span class="hljs-function">
<span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -&gt;</span>
ifThens.pop() <span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> starter <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span>
@tokens.splice (<span class="hljs-keyword">if</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">then</span> i - <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> i), <span class="hljs-number">0</span>, outdent
<span class="hljs-function">
<span class="hljs-title">closeElseTag</span> = <span class="hljs-params">(tokens, i)</span> =&gt;</span>
tlen = ifThens.length
<span class="hljs-keyword">return</span> i <span class="hljs-keyword">unless</span> tlen &gt; <span class="hljs-number">0</span>
lastThen = ifThens.pop()
[, outdentElse] = @indentation tokens[lastThen]</pre></div></div>
</li>
<li id="section-51">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-51">&#182;</a>
</div>
<p>Insert <code>OUTDENT</code> to close inner <code>IF</code>.</p>
</div>
<div class="content"><div class='highlight'><pre> outdentElse[<span class="hljs-number">1</span>] = tlen*<span class="hljs-number">2</span>
tokens.splice(i, <span class="hljs-number">0</span>, outdentElse)</pre></div></div>
</li>
<li id="section-52">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-52">&#182;</a>
</div>
<p>Insert <code>OUTDENT</code> to close outer <code>IF</code>.</p>
</div>
<div class="content"><div class='highlight'><pre> outdentElse[<span class="hljs-number">1</span>] = <span class="hljs-number">2</span>
tokens.splice(i + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, outdentElse)</pre></div></div>
</li>
<li id="section-53">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-53">&#182;</a>
</div>
<p>Remove outdents from the end.</p>
</div>
<div class="content"><div class='highlight'><pre> @detectEnd i + <span class="hljs-number">2</span>,
<span class="hljs-function"><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">'OUTDENT'</span>, <span class="hljs-string">'TERMINATOR'</span>]
(token, i) -&gt;
<span class="hljs-keyword">if</span> @tag(i) <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">'OUTDENT'</span>
tokens.splice i, <span class="hljs-number">2</span>
i + <span class="hljs-number">2</span>
@scanTokens (token, i, tokens) -&gt;
[tag] = token
conditionTag = 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>
@findTagsBackwards(i, [<span class="hljs-string">'IF'</span>, <span class="hljs-string">'WHILE'</span>, <span class="hljs-string">'FOR'</span>, <span class="hljs-string">'UNTIL'</span>, <span class="hljs-string">'SWITCH'</span>, <span class="hljs-string">'WHEN'</span>, <span class="hljs-string">'LEADING_WHEN'</span>, <span class="hljs-string">'['</span>, <span class="hljs-string">'INDEX_START'</span>]) <span class="hljs-keyword">and</span>
<span class="hljs-keyword">not</span> (@findTagsBackwards i, [<span class="hljs-string">'THEN'</span>, <span class="hljs-string">'..'</span>, <span class="hljs-string">'...'</span>])
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span>
<span class="hljs-keyword">if</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'OUTDENT'</span>
tokens.splice i, <span class="hljs-number">1</span>, @indentation()...
@@ -1275,10 +1374,31 @@ blocks are added.</p>
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>)
<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>) <span class="hljs-keyword">and</span>
<span class="hljs-keyword">not</span> conditionTag
starter = tag
[indent, outdent] = @indentation tokens[i]
indent.fromThen = <span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> starter <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span>
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span>
leading_switch_when = @findTagsBackwards(i, [<span class="hljs-string">'LEADING_WHEN'</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>
leading_if_then = @findTagsBackwards(i, [<span class="hljs-string">'IF'</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>
ifThens.push i <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span> <span class="hljs-keyword">and</span> @findTagsBackwards(i, [<span class="hljs-string">'IF'</span>])</pre></div></div>
</li>
<li id="section-54">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-54">&#182;</a>
</div>
<p><code>ELSE</code> tag is not closed.</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'OUTDENT'</span>
i = closeElseTag tokens, i
tokens.splice i + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, indent
@detectEnd i + <span class="hljs-number">2</span>, condition, action
tokens.splice i, <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span>
@@ -1288,11 +1408,11 @@ blocks are added.</p>
</li>
<li id="section-50">
<li id="section-55">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-50">&#182;</a>
<a class="pilcrow" href="#section-55">&#182;</a>
</div>
<p>Tag postfix conditionals as such, so that we can parse them with a
different precedence.</p>
@@ -1320,11 +1440,11 @@ different precedence.</p>
</li>
<li id="section-51">
<li id="section-56">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-51">&#182;</a>
<a class="pilcrow" href="#section-56">&#182;</a>
</div>
<p>Generate the indentation tokens, based on another token on the same line.</p>
@@ -1345,11 +1465,11 @@ different precedence.</p>
</li>
<li id="section-52">
<li id="section-57">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-52">&#182;</a>
<a class="pilcrow" href="#section-57">&#182;</a>
</div>
<p>Look up a tag by token index.</p>
@@ -1360,11 +1480,11 @@ different precedence.</p>
</li>
<li id="section-53">
<li id="section-58">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-53">&#182;</a>
<a class="pilcrow" href="#section-58">&#182;</a>
</div>
<h2 id="constants">Constants</h2>
@@ -1373,11 +1493,11 @@ different precedence.</p>
</li>
<li id="section-54">
<li id="section-59">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-54">&#182;</a>
<a class="pilcrow" href="#section-59">&#182;</a>
</div>
</div>
@@ -1385,11 +1505,11 @@ different precedence.</p>
</li>
<li id="section-55">
<li id="section-60">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-55">&#182;</a>
<a class="pilcrow" href="#section-60">&#182;</a>
</div>
<p>List of the token pairs that must be balanced.</p>
@@ -1410,11 +1530,11 @@ different precedence.</p>
</li>
<li id="section-56">
<li id="section-61">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-56">&#182;</a>
<a class="pilcrow" href="#section-61">&#182;</a>
</div>
<p>The inverse mappings of <code>BALANCED_PAIRS</code> were trying to fix up, so we can
look things up from either end.</p>
@@ -1426,11 +1546,11 @@ look things up from either end.</p>
</li>
<li id="section-57">
<li id="section-62">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-57">&#182;</a>
<a class="pilcrow" href="#section-62">&#182;</a>
</div>
<p>The tokens that signal the start/end of a balanced pair.</p>
@@ -1446,11 +1566,11 @@ EXPRESSION_END = []
</li>
<li id="section-58">
<li id="section-63">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-58">&#182;</a>
<a class="pilcrow" href="#section-63">&#182;</a>
</div>
<p>Tokens that indicate the close of a clause of an expression.</p>
@@ -1461,11 +1581,11 @@ EXPRESSION_END = []
</li>
<li id="section-59">
<li id="section-64">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-59">&#182;</a>
<a class="pilcrow" href="#section-64">&#182;</a>
</div>
<p>Tokens that, if followed by an <code>IMPLICIT_CALL</code>, indicate a function invocation.</p>
@@ -1476,11 +1596,11 @@ EXPRESSION_END = []
</li>
<li id="section-60">
<li id="section-65">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-60">&#182;</a>
<a class="pilcrow" href="#section-65">&#182;</a>
</div>
<p>If preceded by an <code>IMPLICIT_FUNC</code>, indicates a function invocation.</p>
@@ -1500,11 +1620,11 @@ IMPLICIT_UNSPACED_CALL = [<span class="hljs-string">'+'</span>, <span class="hlj
</li>
<li id="section-61">
<li id="section-66">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-61">&#182;</a>
<a class="pilcrow" href="#section-66">&#182;</a>
</div>
<p>Tokens that always mark the end of an implicit call for single-liners.</p>
@@ -1516,11 +1636,11 @@ IMPLICIT_UNSPACED_CALL = [<span class="hljs-string">'+'</span>, <span class="hlj
</li>
<li id="section-62">
<li id="section-67">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-62">&#182;</a>
<a class="pilcrow" href="#section-67">&#182;</a>
</div>
<p>Single-line flavors of block expressions that have unclosed endings.
The grammar cant disambiguate them, so we insert the implicit indentation.</p>
@@ -1533,11 +1653,11 @@ SINGLE_CLOSERS = [<span class="hljs-string">'TERMINATOR'</span>, <span class="
</li>
<li id="section-63">
<li id="section-68">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-63">&#182;</a>
<a class="pilcrow" href="#section-68">&#182;</a>
</div>
<p>Tokens that end a line.</p>
@@ -1548,11 +1668,11 @@ SINGLE_CLOSERS = [<span class="hljs-string">'TERMINATOR'</span>, <span class="
</li>
<li id="section-64">
<li id="section-69">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-64">&#182;</a>
<a class="pilcrow" href="#section-69">&#182;</a>
</div>
<p>Tokens that close open calls when they follow a newline.</p>
@@ -1563,11 +1683,11 @@ SINGLE_CLOSERS = [<span class="hljs-string">'TERMINATOR'</span>, <span class="
</li>
<li id="section-65">
<li id="section-70">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-65">&#182;</a>
<a class="pilcrow" href="#section-70">&#182;</a>
</div>
<p>Tokens that prevent a subsequent indent from ending implicit calls/objects</p>
@@ -1578,11 +1698,11 @@ SINGLE_CLOSERS = [<span class="hljs-string">'TERMINATOR'</span>, <span class="
</li>
<li id="section-66">
<li id="section-71">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-66">&#182;</a>
<a class="pilcrow" href="#section-71">&#182;</a>
</div>
<p>Tokens that are swallowed up by the parser, never leading to code generation.
You can spot these in <code>grammar.coffee</code> because the <code>o</code> function second

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -6,4 +6,4 @@ Object.defineProperty screen, 'height',
get: ->
this.width / this.ratio
set: (val) ->
this.width = val / this.ratio
this.width = val * this.ratio

View File

@@ -1,5 +1,88 @@
## Changelog
```
releaseHeader('2018-04-29', '2.3.0', '2.2.4')
```
* This release adds support for all the new features and syntaxes in ES2018 that werent already possible in CoffeeScript. For all of the below features, make sure that you [transpile](#transpilation) unless you know that your target runtime(s) support each feature.
* Asynchronous iterators are now supported. You can now `yield` an `await` call, e.g. `do -> until file.EOF then yield await file.readLine()`.
* Object splats/destructuring, a.k.a. object rest/spread syntax, has been standardized as part of ES2018 and therefore this release removes the polyfill that had previously been supporting this syntax. Code like `{a, b, rest...} = obj` now outputs more or less just like it appears, rather than being converted into an `Object.assign` call. Note that there are [some subtle differences](https://developers.google.com/web/updates/2017/06/object-rest-spread) between the `Object.assign` polyfill and the native implementation.
* The exponentiation operator, `**`, and exponentiation assignment operator `**=` are new to JavaScript in ES2018. Now code like `a ** 3` is output as it appears, rather than being converted into `Math.pow(a, 3)` as it was before.
* The `s` (dotAll) flag is now supported in regular expressions.
```
releaseHeader('2018-03-29', '2.2.4', '2.2.3')
```
* When the `by` value in a `for` loop is a literal number, e.g. `for x in [2..1] by -1`, fewer checks are necessary to determine if the loop is in range.
* Bugfix for regression in 2.2.0 where a statement inside parentheses, e.g. `(fn(); break) while condition`, was compiling. Pure statements like `break` or `return` cannot turn a parenthesized block into an expression, and should throw an error.
```
releaseHeader('2018-03-11', '2.2.3', '2.2.2')
```
* Bugfix for object destructuring with an empty array as a keys value: `{ key: [] } = obj`.
* Bugfix for array destructuring onto targets attached to `this`: `[ @most... , @penultimate, @last ] = arr`.
```
releaseHeader('2018-02-21', '2.2.2', '2.2.1')
```
* Bugfix for regression in 2.2.0 where a range with a `by` (step) value that increments or decrements in the opposite direction as the range was returning an array containing the first value of the range, whereas it should be returning an empty array. In other words, `x for x in [2..1] by 1` should equal `[]`, not `[2]` (because the step value is positive 1, counting up, whereas the range goes from 2 to 1, counting down).
* Bugfixes for allowing backslashes in `import` and `export` statements and lines that trigger the start of an indented block, like an `if` statement.
```
releaseHeader('2018-02-06', '2.2.1', '2.2.0')
```
* Bugfix for regression in 2.2.0 involving an error thrown by the compiler in certain cases when using destructuring with a splat or expansion in an array.
* Bugfix for regression in 2.2.0 where in certain cases a range iterator variable was declared in the global scope.
```
releaseHeader('2018-02-01', '2.2.0', '2.1.1')
```
* This release fixes *all* currently open bugs, dating as far back as 2014, 2012 and 2011.
* **Potential breaking change:** An inline `if` or `switch` statement with an ambiguous `else`, such as `if no then if yes then alert 1 else alert 2`, now compiles where the `else` always corresponds to the closest open `then`. Previously the behavior of an ambiguous `else` was unpredictable. If your code has any `if … then` or `switch … then` statements with multiple `then`s (and one or more `else`s) the compiled output might be different now, unless you had resolved ambiguity via parentheses. We made this change because the previous behavior was inconsistent and basically a bug: depending on what grammar was where, for example if there was an inline function or something that implied a block, the `else` might bind to an earlier `then` rather than a later `then`. Now an `else` essentially closes a block opened by a `then`, similar to closing an open parenthesis.
* When a required `then` is missing, the error more accurately points out the location of the mistake.
* An error is thrown when the `coffee` command is run in an environment that doesnt support some ES2015 JavaScript features that the CoffeeScript compiler itself requires. This can happen if CoffeeScript is installed in Node older than version 6.
* Destructuring with a non-final splat/spread, e.g. `[open, contents..., close] = tag.split('')` is now output using ES2015 rest syntax.
* Functions named `get` or `set` can be used without parentheses in more cases, including when attached to `this` or `@` or `?.`; or when the first argument is an implicit object, e.g. `@set key: 'val'`.
* Statements such as `break` can now be used inside parentheses, e.g. `(doSomething(); break) while condition` or `(pick(key); break) for key of obj`.
* Bugfix for assigning to a property attached to `this`/`@` in destructuring, e.g. `({@prop = yes, @otherProp = no}) ->`.
* Bugfix for incorrect errors being thrown about calling `super` with a parameter attached to `this` when said parameter is in a lower scope, e.g. `class Child extends Parent then constructor: -> super(-> @prop)`.
* Bugfix to prevent a possible infinite loop when a `for` loop is given a variable to step by, e.g. `for x in [1..3] by step` (as opposed to `by 0.5` or some other primitive numeric value).
* Bugfix to no longer declare iterator variables twice when evaluating a range, e.g. `end = 3; fn [0..end]`.
* Bugfix for incorrect scope of variables in chained calls, e.g. `start(x = 3).then(-> x = 4)`.
* Bugfix for incorrect scope of variables in a function passed to `do`, e.g. `for [1..3] then masked = 10; do -> alert masked`.
* Bugfix to no longer throw a syntax error for a trailing comma in a function call, e.g. `fn arg1, arg2,`.
* Bugfix for an expression in a property access, e.g. `a[!b in c..]`.
* Bugfix to allow a line continuation backslash (`\`) at any point in a `for` line.
```
releaseHeader('2017-12-29', '2.1.1', '2.1.0')
```
* Bugfix to set the correct context for executable class bodies. So in `class @B extends @A then @property = 1`, the `@` in `@property` now refers to the class, not the global object.
* Bugfix where anonymous classes were getting created using the same automatic variable name. They now each receive unique names, so as not to override each other.
```
releaseHeader('2017-12-10', '2.1.0', '2.0.3')
```
* Computed property keys in object literals are now supported: `obj = { ['key' + i]: 42 }`, or `obj = [Symbol.iterator]: -> yield i++`.
* Skipping of array elements, a.k.a. elision, is now supported: `arr = [a, , b]`, or `[, protocol] = url.match /^(.*):\/\//`.
* [JSX fragments syntax](https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html) is now supported.
* Bugfix where `///` within a `#` line comment inside a `///` block regex was erroneously closing the regex, rather than being treated as part of the comment.
* Bugfix for incorrect output for object rest destructuring inside array destructuring.
```
releaseHeader('2017-11-26', '2.0.3', '2.0.2')
```
* Bugfix for `export default` followed by an implicit object that contains an explicit object, for example `exportedMember: { obj... }`.
* Bugfix for `key, val of obj` after an implicit object member, e.g. `foo: bar for key, val of obj`.
* Bugfix for combining array and object destructuring, e.g. `[ ..., {a, b} ] = arr`.
* Bugfix for an edge case where it was possible to create a bound (`=>`) generator function, which should throw an error as such functions arent allowed in ES2015.
* Bugfix for source maps: `.map` files should always have the same base filename as the requested output filename. So `coffee --map --output foo.js test.coffee` should generate `foo.js` and `foo.js.map`.
* Bugfix for incorrect source maps generated when using `--transpile` with `--map` for multiple input files.
* Bugfix for comments at the beginning or end of input into the REPL (`coffee --interactive`).
```
releaseHeader('2017-10-26', '2.0.2', '2.0.1')
```

View File

@@ -1,5 +1,10 @@
### Compatibility
With the exception of [modules](#modules) (`import` and `export` statements) and [JSX](#jsx), all the modern JavaScript 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 [run the tests in your browser](test.html) to see if your browser can do the same. For older browsers or older versions of Node, however, [transpilation](#transpilation) is required.
Most modern JavaScript features that CoffeeScript supports can run natively in Node 7.6+, meaning that Node can run CoffeeScripts output without any further processing required. Here are some notable exceptions:
Support for modern JavaScript syntax is important to ensure compatibility with frameworks that assume modern features. Now that CoffeeScript compiles classes to the `class` keyword, its possible to `extend` a JavaScript 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. Some such features behave slightly differently in JavaScript than they did in CoffeeScript 1; in such cases we are conforming with the JavaScript spec, and weve documented the differences as [breaking changes](#breaking-changes).
* [Modules](#modules) and [JSX](#jsx) always require transpilation.
* [Splats, a.k.a. object rest/spread syntax, for objects](http://coffeescript.org/#splats) are supported by Node 8.6+.
* The [regular expression `s` (dotall) flag](https://github.com/tc39/proposal-regexp-dotall-flag) is supported by Node 9+.
* [Async generator functions](https://github.com/tc39/proposal-async-iteration) are supported by Node 10+.
This list may be incomplete, and excludes versions of Node that support newer features behind flags; please refer to [node.green](http://node.green/) for full details. You can [run the tests in your browser](test.html) to see what your browser supports. It is your responsibility to ensure that your runtime supports the modern features you use; or that you [transpile](#transpilation) your code. When in doubt, transpile.

View File

@@ -1,6 +1,6 @@
## Bound (Fat Arrow) Functions
In JavaScript, the `this` keyword is dynamically scoped to mean the object that the current function is attached to. If you pass a function as a callback or attach it to a different object, the original value of `this` will be lost. If youre not familiar with this behavior, [this Digital Web article](http://64.13.255.16/articles/scope_in_javascript/) gives a good overview of the quirks.
In JavaScript, the `this` keyword is dynamically scoped to mean the object that the current function is attached to. If you pass a function as a callback or attach it to a different object, the original value of `this` will be lost. If youre not familiar with this behavior, [this Digital Web article](https://web.archive.org/web/20150316122013/http://www.digital-web.com/articles/scope_in_javascript) gives a good overview of the quirks.
The fat arrow `=>` can be used to both define a function, and to bind it to the current value of `this`, right on the spot. This is helpful when using callback-based libraries like Prototype or jQuery, for creating iterator functions to pass to `each`, or event-handler functions to use with `on`. Functions created with the fat arrow are able to access properties of the `this` where theyre defined.

View File

@@ -8,6 +8,6 @@ codeFor('modules')
<div id="modules-note" class="bookmark"></div>
Note that the CoffeeScript compiler **does not resolve modules**; writing an `import` or `export` statement in CoffeeScript will produce an `import` or `export` statement in the resulting output. It is your responsibility attach another transpiler, such as [Traceur Compiler](https://github.com/google/traceur-compiler), [Babel](http://babeljs.io/) or [Rollup](https://github.com/rollup/rollup), to convert this ES2015 syntax into code that will work in your target runtimes.
Note that the CoffeeScript compiler **does not resolve modules**; writing an `import` or `export` statement in CoffeeScript will produce an `import` or `export` statement in the resulting output. It is your responsibility to [transpile](#transpilation) this ES2015 syntax into code that will work in your target runtimes.
Also note that any file with an `import` or `export` statement will be output without a [top-level function safety wrapper](#lexical-scope); in other words, importing or exporting modules will automatically trigger [bare](#usage) mode for that file. This is because per the ES2015 spec, `import` or `export` statements must occur at the topmost scope.

View File

@@ -6,12 +6,6 @@ The CoffeeScript literals for objects and arrays look very similar to their Java
codeFor('objects_and_arrays', 'song.join(" … ")')
```
In JavaScript, you cant use reserved words, like `class`, as properties of an object, without quoting them as strings. CoffeeScript notices reserved words used as keys in objects and quotes them for you, so you dont have to worry about it (say, when using jQuery).
```
codeFor('objects_reserved')
```
CoffeeScript has a shortcut for creating objects when you want the key to be set with a variable of the same name.
```

View File

@@ -18,7 +18,7 @@ You can use `in` to test for array presence, and `of` to test for JavaScript obj
In a `for` loop, `from` compiles to the [ES2015 `of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of). (Yes, its unfortunate; the CoffeeScript `of` predates the ES2015 `of`.)
To simplify math expressions, `**` can be used for exponentiation and `//` performs integer division. `%` works just like in JavaScript, while `%%` provides [“dividend dependent modulo”](https://en.wikipedia.org/wiki/Modulo_operation):
To simplify math expressions, `**` can be used for exponentiation and `//` performs floor division. `%` works just like in JavaScript, while `%%` provides [“dividend dependent modulo”](https://en.wikipedia.org/wiki/Modulo_operation):
```
codeFor('modulo')
@@ -39,7 +39,7 @@ All together now:
| `a in b` | `[].indexOf.call(b, a) >= 0` |
| `a of b` | `a in b` |
| `for a from b` | `for (a of b)` |
| `a ** b` | `Math.pow(a, b)` |
| `a ** b` | `a ** b` |
| `a // b` | `Math.floor(a / b)` |
| `a %% b` | `(a % b + b) % b` |

View File

@@ -2,6 +2,4 @@
While its not recommended for serious use, CoffeeScripts may be included directly within the browser using `<script type="text/coffeescript">` tags. The source includes a compressed and minified version of the compiler ([Download current version here, 51k when gzipped](/v<%= majorVersion %>/browser-compiler/coffeescript.js)) as `docs/v<%= majorVersion %>/browser-compiler/coffeescript.js`. Include this file on a page with inline CoffeeScript tags, and it will compile and evaluate them in order.
In fact, the little bit of glue script that runs [Try CoffeeScript](#try), as well as the code examples and other interactive parts of this site, is implemented in just this way. View source and look at the bottom of the page to see the example. Including the script also gives you access to `CoffeeScript.compile()` so you can pop open your JavaScript console and try compiling some strings.
The usual caveats about CoffeeScript apply — your inline scripts will run within a closure wrapper, so if you want to expose global variables or functions, attach them to the `window` object.

View File

@@ -22,4 +22,4 @@ codeFor('array_spread', 'all')
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.
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 and objects since ES2018.

View File

@@ -3,11 +3,11 @@
<%= include('try.html') %>
<div class="container-fluid" id="top">
<div class="row flex-nowrap">
<nav class="sidebar col-lg-3 bg-ribbed-light">
<div class="row flex-nowrap main-row">
<nav class="sidebar bg-ribbed-light">
<%= include('sidebar.html') %>
</nav>
<main class="main col-lg-9 ml-auto">
<main class="main">
<header class="d-none d-lg-block">
<p class="title-logo">
<%= include('logo.svg') %>

View File

@@ -1,7 +1,15 @@
/* Adapted from https://github.com/FarhadG/code-mirror-themes/blob/master/themes/twilight.css */
/* Adapted from https://github.com/FarhadG/code-mirror-themes/blob/master/themes/twilight.css and https://github.com/codemirror/CodeMirror/blob/master/theme/twilight.css */
/* CodeMirror general styling */
.CodeMirror,
.placeholder-code {
/* https://codemirror.net/demo/resize.html */
height: auto;
background: transparent;
font-family: 'Roboto Mono';
font-weight: 400;
line-height: 1.25;
letter-spacing: 0.3px;
color: #f8f8f8;
/* Prevent mobile Safari from zooming in on our code editors; the code is 16px naturally, but somehow being explicit about it prevents the zooming */
@@ -10,7 +18,7 @@
@media (min-width: 768px) {
.CodeMirror,
.placeholder-code {
font-size: 90%; /* Matching Bootstraps font size for code, which calculates to 14.4px */
font-size: 87.5%; /* Matching Bootstraps font size for code, which calculates to 14.4px */
}
}
.CodeMirror-lines {
@@ -21,6 +29,13 @@
margin-bottom: 1.37em;
white-space: pre-wrap;
}
.CodeMirror pre,
pre.placeholder-code {
line-height: 1.4em;
}
.CodeMirror-code:focus {
outline: none;
}
div.CodeMirror-cursor {
border-left: 3px solid #f8f8f8;
}
@@ -30,27 +45,18 @@ div.CodeMirror-cursor {
.CodeMirror-selected {
background: #ddf0ff33;
}
.cm-comment,
.placeholder-code .comment {
font-style: italic;
color: #5f5a60;
}
/* Syntax highlighting */
.cm-keyword,
.placeholder-code .keyword {
color: #cda869;
}
.cm-string,
.placeholder-code .string {
color: #8f9d6a;
}
.cm-property,
.placeholder-code .attribute {
color: #dad085;
}
.cm-atom {
color: #dad085;
}
.cm-number,
.cm-meta,
.placeholder-code .number,
.placeholder-code .built_in,
.placeholder-code .builtin-name,
@@ -61,6 +67,18 @@ div.CodeMirror-cursor {
.placeholder-code .link {
color: #dad085;
}
.cm-def {
color: #f8f8f8;
}
span.cm-variable-2,
span.cm-tag {
color: #f8f8f8;
}
span.cm-variable-3,
span.cm-def,
span.cm-type {
color: #f8f8f8;
}
.cm-operator,
.placeholder-code .punctuation,
.placeholder-code .symbol,
@@ -69,9 +87,50 @@ div.CodeMirror-cursor {
.placeholder-code .operator {
color: #cda869;
}
.cm-comment,
.placeholder-code .comment {
font-style: italic;
color: #5f5a60;
}
.cm-string,
.cm-string-2,
.placeholder-code .string {
color: #8f9d6a;
}
.cm-property,
.placeholder-code .attribute {
color: #dad085;
}
.cm-builtin {
color: #cda869;
}
.cm-tag {
color: #997643;
}
.cm-attribute {
color: #d6bb6d;
}
.cm-header {
color: #FF6400;
}
.cm-hr {
color: #AEAEAE;
}
.cm-link {
color: #ad9361;
font-style: italic;
text-decoration: none;
}
.cm-error {
border-bottom: 1px solid rgba(142, 22, 22, 0.67);
}
/* Uneditable code blocks are inverted, so use darker versions of the above */
.uneditable-code-block code {
padding: 0;
}
.uneditable-code-block .comment {
font-style: italic;
color: #837B85;

View File

@@ -1,6 +1,18 @@
unless window.location.origin # Polyfill `location.origin` for IE < 11
window.location.origin = "#{window.location.protocol}//#{window.location.hostname}"
# Initialize Google Analytics
window.GA_TRACKING_ID = 'UA-106156830-1'
window.dataLayer ?= []
window.gtag = ->
window.dataLayer.push arguments
return
window.gtag 'js', new Date()
window.gtag 'config', window.GA_TRACKING_ID
# Initialize the CoffeeScript docs interactions
$(document).ready ->
# Mobile navigation
toggleSidebar = ->
@@ -21,14 +33,14 @@ $(document).ready ->
# Initialize Scrollspy for sidebar navigation; https://getbootstrap.com/docs/4.0/components/scrollspy/
# See also http://www.codingeverything.com/2014/02/BootstrapDocsSideBar.html and http://jsfiddle.net/KyleMit/v6zhz/
$('body').scrollspy
$('.main').scrollspy
target: '#contents'
offset: Math.round $('main').css('padding-top').replace('px', '')
initializeScrollspyFromHash = (hash) ->
$("#contents a.active[href!='#{hash}']").removeClass 'show'
$(window).on 'activate.bs.scrollspy', (event, target) -> # Why `window`? https://github.com/twbs/bootstrap/issues/20086
$('.main').on 'activate.bs.scrollspy', (event, target) ->
# We only want one active link in the nav
$("#contents a.active[href!='#{target.relatedTarget}']").removeClass 'show'
$target = $("#contents a[href='#{target.relatedTarget}']")
@@ -165,6 +177,11 @@ $(document).ready ->
toggleTry yes
$('[data-close="try"]').click closeTry
$('[data-action="scroll-to-top"]').click (event) ->
return if $('#try').hasClass('show')
$('.main')[0].scrollTop = 0
setTimeout clearHash, 10
clearHash = ->
window.history.replaceState {}, document.title, window.location.pathname

View File

@@ -79,14 +79,18 @@ button:focus, .navbar-dark .navbar-toggler:focus {
}
/*
* Layout; based on https://codepen.io/Sphinxxxx/pen/WjwbEO
*/
.main-row {
height: calc(100vh - 3.5rem);
}
/*
* Sidebar
*/
.sidebar {
position: fixed;
top: 3.5em;
height: calc(100% - 3.5rem);
/* Scrollable contents if viewport is shorter than content */
overflow-y: auto;
overflow-x: hidden;
@@ -102,6 +106,9 @@ button:focus, .navbar-dark .navbar-toggler:focus {
}
@media screen and (max-width: 991px) {
.sidebar {
position: fixed;
top: 3.5em;
height: calc(100% - 3.5rem);
left: -100%;
transition: 0.25s left ease-in-out;
}
@@ -109,6 +116,11 @@ button:focus, .navbar-dark .navbar-toggler:focus {
left: 0;
}
}
@media (min-width: 992px) {
.sidebar {
flex: 0 0 auto;
}
}
.contents {
padding: 0.5em 0 0.5em 0.5em;
@@ -139,10 +151,13 @@ button:focus, .navbar-dark .navbar-toggler:focus {
*/
.main {
max-width: 100%;
padding: 1.3em;
}
@media (min-width: 992px) {
.main {
flex: 1 1 auto;
overflow: auto;
padding-right: 2em;
padding-left: 2em;
}
@@ -221,12 +236,6 @@ h3, h4, h2 time {
font-weight: 400;
}
.main section {
/* Offset the anchor so that clicking on the sidebar links dont hide the heading under the header bar */
padding-top: 2.3rem;
margin-top: -2.3rem;
}
@media (min-width: 1200px) {
.main > header, .main section > h2, .main section > h3, .main section > h4, .main section > p, .main section > blockquote, .main section > ul, .main section > table {
max-width: 80%;
@@ -246,7 +255,7 @@ code {
/*
* Code examples
* Chrome around code examples; see code.css for the styling of the code blocks themselves
*/
textarea {
@@ -281,31 +290,7 @@ textarea {
.play-button {
height: 1em;
}
.CodeMirror pre, pre.placeholder-code {
line-height: 1.4em;
}
.CodeMirror,
.placeholder-code {
/* https://codemirror.net/demo/resize.html */
height: auto;
background: transparent;
font-family: 'Roboto Mono';
font-weight: 400;
line-height: 1.25;
font-size: 0.9em;
}
@media (min-width: 768px) {
.CodeMirror,
.placeholder-code {
font-size: 1em;
}
}
.CodeMirror-code:focus {
outline: none;
width: 1em;
}
.javascript-output-column .CodeMirror-cursor {
@@ -324,6 +309,8 @@ textarea {
right: 0;
opacity: 0;
transition: opacity 0.15s ease-in-out;
z-index: -1001;
background-color: #2f2625;
}
.try-coffeescript.show {
opacity: 1;

View File

@@ -2,5 +2,5 @@
<title>
CoffeeScript Logo
</title>
<path d="M106 228.6c.5 2.3-.9 4.4-5 6.5-5.5-3.1-16.9-4.4-26.7-3.5-10.4.9-19.4 4.2-17.9 11.3 1.5 7.1 11.7 11 29.5 9.5 43.6-3.8 43.4-33.3 107.4-39 49.8-4.4 77.8 11 81.8 29.7 3.1 14.7-9.1 28.6-45.2 31.8-32 2.8-50.7-5.6-52.6-14.6-1-4.5 1.8-11.3 17.2-13.1 1.5 7 10.6 14.4 31.1 12.6 14.8-1.3 27.6-6.6 25.9-14.9-1.8-8.6-17.7-13.7-42.6-11.5-50.7 4.5-63.2 32.5-106.8 36.3-30.8 2.7-55.9-8.5-59.4-25.1-1.3-6.1-1.4-21 31.2-23.9 17.1-1.5 30.7 1.5 32.1 7.9zM-56.4 402.5c-14.3 18-20.4 38.8-19.2 59.2 1.2 20.4 11.4 37.1 26.9 50.2C-32 525-14 528.6 6.4 525c7.8-1.2 16.7-5.3 24.5-7.8-16.7 0-31-5.3-44.9-16.7-15.5-11.4-25.7-26.9-28.2-46.1-3.7-18 0-34.7 10.2-49 11.4-14.3 25.7-22 44.9-24.5 19.2-1.2 35.9 3.7 52.6 15.5-3.7-5.3-9-9-14.3-14.3-16.7-11.4-34.7-16.7-56.7-11.4-19.9 3.6-36.7 13.8-50.9 31.8zm223.6-96.3c-53.9 0-101.6-5.3-136.3-13.1-37.1-9-56.7-19.2-56.7-32.2 0-5.3 2.4-10.2 10.2-15.5-23.3 9-35.9 16.7-35.9 28.2 1.2 13.1 22 25.7 64.5 35.9 40 10.2 91.4 15.5 153 15.5 62.8 0 113-5.3 153-15.5 42.4-10.2 62.8-23.3 62.8-35.9 0-9-9-18-25.7-24.5 3.7 2.4 6.5 6.5 6.5 11.4 0 13.1-19.2 23.3-57.9 32.2-36 8.2-82.1 13.5-137.5 13.5zm153 35.9c-40 9-91.4 15.5-153 15.5-62.8 0-114.2-6.5-154.2-15.5-35.9-9-55.1-19.2-61.6-29.4 6.5 44.9 22 87.3 42.4 124.8 15.5 23.3 31 43.7 46.1 65.7 6.5 13.1 11.4 25.7 14.3 38.8 10.2 14.3 24.5 23.3 42.4 28.2 22 7.8 44.9 11.4 68.1 10.2h2.4c23.3 1.2 47.7-2.4 70.6-10.2 16.7-5.3 31-14.3 41.2-28.2h1.2c2.4-13.1 6.5-25.7 13.1-38.8 15.5-22 31-42.4 46.1-65.7 20.4-37.1 34.7-79.6 42.4-124.8-7.7 11.4-26.9 21.6-61.5 29.4z"/>
<path d="M106 228.6c.5 2.3-.9 4.4-5 6.5-5.5-3.1-16.9-4.4-26.7-3.5-10.4.9-19.4 4.2-17.9 11.3 1.5 7.1 11.7 11 29.5 9.5 43.6-3.8 43.4-33.3 107.4-39 49.8-4.4 77.8 11 81.8 29.7 3.1 14.7-9.1 28.6-45.2 31.8-32 2.8-50.7-5.6-52.6-14.6-1-4.5 1.8-11.3 17.2-13.1 1.5 7 10.6 14.4 31.1 12.6 14.8-1.3 27.6-6.6 25.9-14.9-1.8-8.6-17.7-13.7-42.6-11.5-50.7 4.5-63.2 32.5-106.8 36.3-30.8 2.7-55.9-8.5-59.4-25.1-1.3-6.1-1.4-21 31.2-23.9 17.1-1.5 30.7 1.5 32.1 7.9zM-56.4 402.5c-14.3 18-20.4 38.8-19.2 59.2 1.2 20.4 11.4 37.1 26.9 50.2C-32 525-14 528.6 6.4 525c7.8-1.2 16.7-5.3 24.5-7.8-16.7 0-31-5.3-44.9-16.7-15.5-11.4-25.7-26.9-28.2-46.1-3.7-18 0-34.7 10.2-49 11.4-14.3 25.7-22 44.9-24.5 19.2-1.2 35.9 3.7 52.6 15.5-3.7-5.3-9-9-14.3-14.3-16.7-11.4-34.7-16.7-56.7-11.4-19.9 3.6-36.7 13.8-50.9 31.8zm223.6-96.3c-53.9 0-101.6-5.3-136.3-13.1-37.1-9-56.7-19.2-56.7-32.2 0-5.3 2.4-10.2 10.2-15.5-23.3 9-35.9 16.7-35.9 28.2 1.2 13.1 22 25.7 64.5 35.9 40 10.2 91.4 15.5 153 15.5 62.8 0 113-5.3 153-15.5 42.4-10.2 62.8-23.3 62.8-35.9 0-9-9-18-25.7-24.5 3.7 2.4 6.5 6.5 6.5 11.4 0 13.1-19.2 23.3-57.9 32.2-36 8.2-82.1 13.5-137.5 13.5zm153 35.9c-40 9-91.4 15.5-153 15.5-62.8 0-114.2-6.5-154.2-15.5-35.9-9-55.1-19.2-61.6-29.4 6.5 44.9 22 87.3 42.4 124.8 15.5 23.3 31 43.7 46.1 65.7 6.5 13.1 11.4 25.7 14.3 38.8 10.2 14.3 24.5 23.3 42.4 28.2 22 7.8 44.9 11.4 68.1 10.2h2.4c23.3 1.2 47.7-2.4 70.6-10.2 16.7-5.3 31-14.3 41.2-28.2h1.2c2.4-13.1 6.5-25.7 13.1-38.8 15.5-22 31-42.4 46.1-65.7 20.4-37.1 34.7-79.6 42.4-124.8-7.7 11.4-26.9 21.6-61.5 29.4z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -2,5 +2,5 @@
<title>
CoffeeScript Logo
</title>
<path d="M21.7 351.1c.1.6-.2 1.1-1.2 1.6-1.3-.7-4.1-1.1-6.4-.9-2.5.2-4.6 1-4.3 2.7.4 1.7 2.8 2.7 7.1 2.3 10.5-.9 10.4-8 25.8-9.4 12-1.1 18.7 2.6 19.6 7.1.7 3.5-2.2 6.9-10.9 7.6-7.7.7-12.2-1.4-12.6-3.5-.2-1.1.4-2.7 4.1-3.1.4 1.7 2.5 3.5 7.5 3 3.6-.3 6.6-1.6 6.2-3.6-.4-2.1-4.2-3.3-10.2-2.8-12.2 1.1-15.2 7.8-25.6 8.7-7.4.7-13.4-2-14.2-6-.3-1.5-.3-5 7.5-5.7 4-.3 7.2.4 7.6 2zm-39 41.8c-3.4 4.3-4.9 9.3-4.6 14.2.3 4.9 2.7 8.9 6.5 12 4 3.1 8.3 4 13.2 3.1 1.9-.3 4-1.3 5.9-1.9-4 0-7.4-1.3-10.8-4-3.7-2.7-6.2-6.5-6.8-11.1-.9-4.3 0-8.3 2.4-11.8 2.7-3.4 6.2-5.3 10.8-5.9 4.6-.3 8.6.9 12.6 3.7-.9-1.3-2.2-2.2-3.4-3.4-4-2.7-8.3-4-13.6-2.7-4.8 1-8.8 3.5-12.2 7.8zm53.6-23.1c-12.9 0-24.4-1.3-32.7-3.1-8.9-2.2-13.6-4.6-13.6-7.7 0-1.3.6-2.4 2.4-3.7-5.6 2.2-8.6 4-8.6 6.8.3 3.1 5.3 6.2 15.5 8.6 9.6 2.4 21.9 3.7 36.7 3.7 15.1 0 27.1-1.3 36.7-3.7 10.2-2.4 15.1-5.6 15.1-8.6 0-2.2-2.2-4.3-6.2-5.9.9.6 1.6 1.6 1.6 2.7 0 3.1-4.6 5.6-13.9 7.7-8.6 1.9-19.6 3.2-33 3.2zm36.8 8.6c-9.6 2.2-21.9 3.7-36.7 3.7-15.1 0-27.4-1.6-37-3.7-8.6-2.2-13.2-4.6-14.8-7.1 1.6 10.8 5.3 21 10.2 30 3.7 5.6 7.4 10.5 11.1 15.8 1.6 3.1 2.7 6.2 3.4 9.3 2.4 3.4 5.9 5.6 10.2 6.8 5.3 1.9 10.8 2.7 16.4 2.4h.6c5.6.3 11.5-.6 16.9-2.4 4-1.3 7.4-3.4 9.9-6.8h.3c.6-3.1 1.6-6.2 3.1-9.3 3.7-5.3 7.4-10.2 11.1-15.8 4.9-8.9 8.3-19.1 10.2-30-2 2.8-6.6 5.2-14.9 7.1zm106.2 30.1c-4.8 12.1-17.6 16.9-25.9 16.9-13.4 0-19.9-6-19.9-22.3 0-16.5 7.9-47.3 31.7-47.3 8.5 0 15.2 3.3 15.2 12.1 0 4.8-1.8 8.3-6.4 8.3-1.5 0-3.4-.4-5.2-2.4 2.2-1.1 4.2-4.9 4.2-8.3 0-2.9-1.5-5.6-5.6-5.6-10 0-18.9 23.9-18.9 42.4 0 8.3 2.2 14.2 10.9 14.2 7.1 0 13.5-3.4 17.7-9.1l2.2 1.1zm32.9-16.3c.4.2.7.2 1 .2 4.2 0 10.1-2.7 14-5.5l.8 2.4c-3.4 3.7-9.5 6.5-16.1 7.5-1.5 16.8-10.6 27.3-21.7 27.3-8.4 0-14.5-4-14.5-14.4 0-10.5 6.2-32.2 24.9-32.2 7.8.3 11.6 5.3 11.6 14.7zm-7.7 5c-1.9-.5-2.4-2-2.4-3.8 0-2.5 1.2-4.2 2.8-4.9-.2-3.8-1.1-5.3-3.4-5.3-6.5 0-12 16.6-12 25.6 0 6 1.2 7.3 4.6 7.3 4.2.1 8.9-8 10.4-18.9zm-6.6 39.7c0-8.3 7.1-11 15.8-13.6l10.9-51.9c2.7-13 10.6-15.5 16.5-15.5 4.1 0 8 2.2 9.7 5.7 3.6-4.6 8.4-5.7 12.4-5.7 5.6 0 10.8 3.9 10.8 9.8 0 1.5-.1 2.6-.3 3.7h-4.3c.1-.9.2-1.7.2-2.4 0-2.1-1.7-3.1-3.4-3.1-2 0-4.8 1.1-6.2 7.1l-1.7 7.4h9.1l-.8 3.6h-9l-10.3 49.1c-2.7 13-10.6 15.5-16.5 15.5-5.2 0-8.3-2.3-9.8-5.7-3.5 4.6-8.3 5.7-12.3 5.7-5.6.1-10.8-3.8-10.8-9.7zm9.1 1.8c1.9 0 4.2-1.8 5.4-7.1l1.1-5.3c-5.7 2-10.1 4.4-10.1 9.4 0 1.2 1.7 3 3.6 3zm21.7 0c1.9 0 4.2-1.8 5.4-7.1l2.2-10.4-9.4 1.8-1.8 8.3c-.5 2.1-1.1 4-1.8 5.6.9 1.3 3 1.8 5.4 1.8zm-1.4-18l9.4-1.7 7.7-36.8h-9l-8.1 38.5zm16.6-56.7c-2 0-4.8 1.1-6.2 7.1l-1.7 7.4h9l2.1-9.5c.2-.7.2-1.3.2-2 .1-2-1.5-3-3.4-3zm37.9 53c7.1 0 11.6-4 16.1-9.2h3.1c-5.2 8.3-12.9 16.8-25 16.8-8.5 0-14.2-4.2-14.2-14.5 0-10.5 5.9-32.3 24.6-32.3 8.1 0 10 4.2 10 8.7 0 10.5-10 18.5-20.9 19.2-.1 1.3-.2 2.5-.2 3.6 0 6.2 2.2 7.7 6.5 7.7zm5.3-34.4c-4.6 0-9.1 9.7-10.9 18.7 7-.5 13.2-7.4 13.2-15 0-2.2-.5-3.7-2.3-3.7zm28.6 33.4c3.4 0 7.8-2.3 10.8-4.8-2 10.4-8.4 13.4-15.8 13.4-8.4 0-14.1-4.2-14.1-14.5 0-10.5 5.9-32.3 24.6-32.3 8.1 0 10 4.2 10 8.7 0 10.6-10 18.5-20.9 19.2-.1.9-.2 2-.2 2.7 0 5.7 2.5 7.6 5.6 7.6zm6.2-33.4c-4.5 0-9.1 10.1-11 18.7 7.1-.4 13.3-7.3 13.3-15 0-2.2-.6-3.7-2.3-3.7zm51.3-6.7c-1.7 0-3-.6-4.2-1.9 2.4-1.5 4.1-4.8 4.1-7.8 0-3.1-1.8-6.1-6.8-6.1s-8.3 2.8-8.3 8.2c0 13.3 20.5 15.2 20.5 34.8 0 15.3-12.3 22.7-25.6 22.7-10.4 0-19.3-4.5-19.3-15.7 0-9.8 7-14.9 13.3-14.9 3.1 0 7.7 1.3 8 6-4.9 0-10.7 2.3-10.7 8.5 0 4.5 2.9 8.7 8.7 8.7 6.1 0 10.6-4.4 10.6-12 0-15.6-18.6-21.1-18.6-34.5 0-9.5 9.3-16.3 21-16.3 4.3 0 14.6.9 14.6 10.9.1 5.5-2.8 9.4-7.3 9.4zm36.2 10.3c0-2.3-.8-3.7-2.5-3.7-5.7 0-11.7 16.6-11.7 26.7 0 6.2 2.2 7.6 6.6 7.6 7.1 0 11.6-4 16.1-9.2h3.1c-5.2 8.3-12.9 16.8-25 16.8-8.5 0-14.2-4.2-14.2-14.5 0-10.6 6-32.3 24.5-32.3 8.1 0 10.1 4.2 10.1 8.3 0 4.4-2.2 6.7-4.8 6.7-1 0-2.1-.4-3.1-1.1.5-1.9.9-3.6.9-5.3zm27.7-7.6l-1.2 5.7c3.1-2.7 6.7-5.7 11-5.7 4.1 0 6.3 3.3 6.3 6.9 0 3.1-2.1 6.7-6.6 6.7-5.1 0-2.5-6-5.3-6-2.7 0-4.4 1.4-6.7 3.4l-7.2 34.6h-13.1l9.6-45.4 13.2-.2zm34.2 0l-6.6 30.9c-.3 1.2-.4 2.1-.4 2.9 0 2.5 1.2 3.3 3.7 3.3 3.5 0 6.9-3.4 8.1-8h3.8c-5.2 14.8-14.2 16.8-19.1 16.8-5.5 0-9.7-3.2-9.7-10.9 0-1.8.3-3.7.7-5.9l6.2-29.2 13.3.1zm-4.1-19.4c4 0 7.2 3.2 7.2 7.2s-3.2 7.1-7.2 7.1-7.1-3.1-7.1-7.1c-.1-4 3.2-7.2 7.1-7.2zm29.1 16l-1.5 6.9c2.6-2.3 6.1-3.9 10.7-3.9 6.2 0 11.1 3.5 11.1 14.4 0 12.2-4.7 32.1-22.3 32.1-4.5 0-6.8-1.6-7.7-3.2l-4.7 22.1-13.7 3.2 15.2-71.5 12.9-.1zm7.8 17c0-7-2.9-7.5-4.5-7.5-2 0-4.5 1.6-6.3 4.4l-5.4 25.5c.4 1 1.4 2.1 3.4 2.1 9.7 0 12.8-15.9 12.8-24.5zm27.8 17.3c-.3 1.1-.5 2.2-.5 3.1 0 1.9.7 3.2 3.1 3.2.7 0 1.7 0 2.4-.3-2.5 7.8-6.6 8.9-9.6 8.9-6.4 0-9.1-4.4-9.1-10.3 0-1.6.2-3.1.6-4.8l5.8-27.2h-3l.7-3.6h3L528 366l13.4-1.9s-1.4 6.2-3.1 14.4h5.5l-.7 3.6h-5.5l-5.7 27.4z"/>
<path d="M21.7 351.1c.1.6-.2 1.1-1.2 1.6-1.3-.7-4.1-1.1-6.4-.9-2.5.2-4.6 1-4.3 2.7.4 1.7 2.8 2.7 7.1 2.3 10.5-.9 10.4-8 25.8-9.4 12-1.1 18.7 2.6 19.6 7.1.7 3.5-2.2 6.9-10.9 7.6-7.7.7-12.2-1.4-12.6-3.5-.2-1.1.4-2.7 4.1-3.1.4 1.7 2.5 3.5 7.5 3 3.6-.3 6.6-1.6 6.2-3.6-.4-2.1-4.2-3.3-10.2-2.8-12.2 1.1-15.2 7.8-25.6 8.7-7.4.7-13.4-2-14.2-6-.3-1.5-.3-5 7.5-5.7 4-.3 7.2.4 7.6 2zm-39 41.8c-3.4 4.3-4.9 9.3-4.6 14.2.3 4.9 2.7 8.9 6.5 12 4 3.1 8.3 4 13.2 3.1 1.9-.3 4-1.3 5.9-1.9-4 0-7.4-1.3-10.8-4-3.7-2.7-6.2-6.5-6.8-11.1-.9-4.3 0-8.3 2.4-11.8 2.7-3.4 6.2-5.3 10.8-5.9 4.6-.3 8.6.9 12.6 3.7-.9-1.3-2.2-2.2-3.4-3.4-4-2.7-8.3-4-13.6-2.7-4.8 1-8.8 3.5-12.2 7.8zm53.6-23.1c-12.9 0-24.4-1.3-32.7-3.1-8.9-2.2-13.6-4.6-13.6-7.7 0-1.3.6-2.4 2.4-3.7-5.6 2.2-8.6 4-8.6 6.8.3 3.1 5.3 6.2 15.5 8.6 9.6 2.4 21.9 3.7 36.7 3.7 15.1 0 27.1-1.3 36.7-3.7 10.2-2.4 15.1-5.6 15.1-8.6 0-2.2-2.2-4.3-6.2-5.9.9.6 1.6 1.6 1.6 2.7 0 3.1-4.6 5.6-13.9 7.7-8.6 1.9-19.6 3.2-33 3.2zm36.8 8.6c-9.6 2.2-21.9 3.7-36.7 3.7-15.1 0-27.4-1.6-37-3.7-8.6-2.2-13.2-4.6-14.8-7.1 1.6 10.8 5.3 21 10.2 30 3.7 5.6 7.4 10.5 11.1 15.8 1.6 3.1 2.7 6.2 3.4 9.3 2.4 3.4 5.9 5.6 10.2 6.8 5.3 1.9 10.8 2.7 16.4 2.4h.6c5.6.3 11.5-.6 16.9-2.4 4-1.3 7.4-3.4 9.9-6.8h.3c.6-3.1 1.6-6.2 3.1-9.3 3.7-5.3 7.4-10.2 11.1-15.8 4.9-8.9 8.3-19.1 10.2-30-2 2.8-6.6 5.2-14.9 7.1zm106.2 30.1c-4.8 12.1-17.6 16.9-25.9 16.9-13.4 0-19.9-6-19.9-22.3 0-16.5 7.9-47.3 31.7-47.3 8.5 0 15.2 3.3 15.2 12.1 0 4.8-1.8 8.3-6.4 8.3-1.5 0-3.4-.4-5.2-2.4 2.2-1.1 4.2-4.9 4.2-8.3 0-2.9-1.5-5.6-5.6-5.6-10 0-18.9 23.9-18.9 42.4 0 8.3 2.2 14.2 10.9 14.2 7.1 0 13.5-3.4 17.7-9.1l2.2 1.1zm32.9-16.3c.4.2.7.2 1 .2 4.2 0 10.1-2.7 14-5.5l.8 2.4c-3.4 3.7-9.5 6.5-16.1 7.5-1.5 16.8-10.6 27.3-21.7 27.3-8.4 0-14.5-4-14.5-14.4 0-10.5 6.2-32.2 24.9-32.2 7.8.3 11.6 5.3 11.6 14.7zm-7.7 5c-1.9-.5-2.4-2-2.4-3.8 0-2.5 1.2-4.2 2.8-4.9-.2-3.8-1.1-5.3-3.4-5.3-6.5 0-12 16.6-12 25.6 0 6 1.2 7.3 4.6 7.3 4.2.1 8.9-8 10.4-18.9zm-6.6 39.7c0-8.3 7.1-11 15.8-13.6l10.9-51.9c2.7-13 10.6-15.5 16.5-15.5 4.1 0 8 2.2 9.7 5.7 3.6-4.6 8.4-5.7 12.4-5.7 5.6 0 10.8 3.9 10.8 9.8 0 1.5-.1 2.6-.3 3.7h-4.3c.1-.9.2-1.7.2-2.4 0-2.1-1.7-3.1-3.4-3.1-2 0-4.8 1.1-6.2 7.1l-1.7 7.4h9.1l-.8 3.6h-9l-10.3 49.1c-2.7 13-10.6 15.5-16.5 15.5-5.2 0-8.3-2.3-9.8-5.7-3.5 4.6-8.3 5.7-12.3 5.7-5.6.1-10.8-3.8-10.8-9.7zm9.1 1.8c1.9 0 4.2-1.8 5.4-7.1l1.1-5.3c-5.7 2-10.1 4.4-10.1 9.4 0 1.2 1.7 3 3.6 3zm21.7 0c1.9 0 4.2-1.8 5.4-7.1l2.2-10.4-9.4 1.8-1.8 8.3c-.5 2.1-1.1 4-1.8 5.6.9 1.3 3 1.8 5.4 1.8zm-1.4-18l9.4-1.7 7.7-36.8h-9l-8.1 38.5zm16.6-56.7c-2 0-4.8 1.1-6.2 7.1l-1.7 7.4h9l2.1-9.5c.2-.7.2-1.3.2-2 .1-2-1.5-3-3.4-3zm37.9 53c7.1 0 11.6-4 16.1-9.2h3.1c-5.2 8.3-12.9 16.8-25 16.8-8.5 0-14.2-4.2-14.2-14.5 0-10.5 5.9-32.3 24.6-32.3 8.1 0 10 4.2 10 8.7 0 10.5-10 18.5-20.9 19.2-.1 1.3-.2 2.5-.2 3.6 0 6.2 2.2 7.7 6.5 7.7zm5.3-34.4c-4.6 0-9.1 9.7-10.9 18.7 7-.5 13.2-7.4 13.2-15 0-2.2-.5-3.7-2.3-3.7zm28.6 33.4c3.4 0 7.8-2.3 10.8-4.8-2 10.4-8.4 13.4-15.8 13.4-8.4 0-14.1-4.2-14.1-14.5 0-10.5 5.9-32.3 24.6-32.3 8.1 0 10 4.2 10 8.7 0 10.6-10 18.5-20.9 19.2-.1.9-.2 2-.2 2.7 0 5.7 2.5 7.6 5.6 7.6zm6.2-33.4c-4.5 0-9.1 10.1-11 18.7 7.1-.4 13.3-7.3 13.3-15 0-2.2-.6-3.7-2.3-3.7zm51.3-6.7c-1.7 0-3-.6-4.2-1.9 2.4-1.5 4.1-4.8 4.1-7.8 0-3.1-1.8-6.1-6.8-6.1s-8.3 2.8-8.3 8.2c0 13.3 20.5 15.2 20.5 34.8 0 15.3-12.3 22.7-25.6 22.7-10.4 0-19.3-4.5-19.3-15.7 0-9.8 7-14.9 13.3-14.9 3.1 0 7.7 1.3 8 6-4.9 0-10.7 2.3-10.7 8.5 0 4.5 2.9 8.7 8.7 8.7 6.1 0 10.6-4.4 10.6-12 0-15.6-18.6-21.1-18.6-34.5 0-9.5 9.3-16.3 21-16.3 4.3 0 14.6.9 14.6 10.9.1 5.5-2.8 9.4-7.3 9.4zm36.2 10.3c0-2.3-.8-3.7-2.5-3.7-5.7 0-11.7 16.6-11.7 26.7 0 6.2 2.2 7.6 6.6 7.6 7.1 0 11.6-4 16.1-9.2h3.1c-5.2 8.3-12.9 16.8-25 16.8-8.5 0-14.2-4.2-14.2-14.5 0-10.6 6-32.3 24.5-32.3 8.1 0 10.1 4.2 10.1 8.3 0 4.4-2.2 6.7-4.8 6.7-1 0-2.1-.4-3.1-1.1.5-1.9.9-3.6.9-5.3zm27.7-7.6l-1.2 5.7c3.1-2.7 6.7-5.7 11-5.7 4.1 0 6.3 3.3 6.3 6.9 0 3.1-2.1 6.7-6.6 6.7-5.1 0-2.5-6-5.3-6-2.7 0-4.4 1.4-6.7 3.4l-7.2 34.6h-13.1l9.6-45.4 13.2-.2zm34.2 0l-6.6 30.9c-.3 1.2-.4 2.1-.4 2.9 0 2.5 1.2 3.3 3.7 3.3 3.5 0 6.9-3.4 8.1-8h3.8c-5.2 14.8-14.2 16.8-19.1 16.8-5.5 0-9.7-3.2-9.7-10.9 0-1.8.3-3.7.7-5.9l6.2-29.2 13.3.1zm-4.1-19.4c4 0 7.2 3.2 7.2 7.2s-3.2 7.1-7.2 7.1-7.1-3.1-7.1-7.1c-.1-4 3.2-7.2 7.1-7.2zm29.1 16l-1.5 6.9c2.6-2.3 6.1-3.9 10.7-3.9 6.2 0 11.1 3.5 11.1 14.4 0 12.2-4.7 32.1-22.3 32.1-4.5 0-6.8-1.6-7.7-3.2l-4.7 22.1-13.7 3.2 15.2-71.5 12.9-.1zm7.8 17c0-7-2.9-7.5-4.5-7.5-2 0-4.5 1.6-6.3 4.4l-5.4 25.5c.4 1 1.4 2.1 3.4 2.1 9.7 0 12.8-15.9 12.8-24.5zm27.8 17.3c-.3 1.1-.5 2.2-.5 3.1 0 1.9.7 3.2 3.1 3.2.7 0 1.7 0 2.4-.3-2.5 7.8-6.6 8.9-9.6 8.9-6.4 0-9.1-4.4-9.1-10.3 0-1.6.2-3.1.6-4.8l5.8-27.2h-3l.7-3.6h3L528 366l13.4-1.9s-1.4 6.2-3.1 14.4h5.5l-.7 3.6h-5.5l-5.7 27.4z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -1,5 +1,5 @@
<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-dark bg-ribbed-dark site-navbar">
<a class="navbar-brand" href="#" data-close="try"><%= include('logo.svg') %></a>
<a class="navbar-brand" href="#" data-close="try" data-action="scroll-to-top"><%= include('logo.svg') %></a>
<button class="navbar-toggler" type="button" data-toggle="offcanvas" data-close="try" aria-label="Toggle sidebar">
<span class="navbar-toggler-icon"></span>
</button>

View File

@@ -1,3 +1,3 @@
<svg class="play-button" viewBox="0 0 24 24">
<path d="M2.56-0.01v24.02L21.44 11.98 2.56-0.01z"/>
<path d="M2.56-0.01v24.02L21.44 11.98 2.56-0.01z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 112 B

View File

@@ -1,19 +1,9 @@
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" integrity="sha384-xBuQ/xzmlsLoJpyjoggmTEz8OWUFM0/RC5BsqQBDX2v5cMvDHcMakNTNrHIW2I5f" crossorigin="anonymous"></script>
<script>
window.Popper = {}; // Remove if we want to use Bootstrap tooltips
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/combine/npm/codemirror@5.29.0/lib/codemirror.js,npm/codemirror@5.29.0/mode/coffeescript/coffeescript.js,npm/codemirror@5.29.0/addon/lint/coffeescript-lint.js,npm/codemirror@5.29.0/mode/javascript/javascript.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/combine/npm/codemirror@5.37.0/lib/codemirror.js,npm/codemirror@5.37.0/mode/coffeescript/coffeescript.js,npm/codemirror@5.37.0/addon/lint/coffeescript-lint.js,npm/codemirror@5.37.0/mode/javascript/javascript.js"></script>
<script src="browser-compiler/coffeescript.js"></script>
<script>
<%= includeScript('docs.coffee') %>
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-106156830-1"></script>
<script>
window.GA_TRACKING_ID = 'UA-106156830-1';
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', GA_TRACKING_ID);
</script>

View File

@@ -1,8 +1,8 @@
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
<!-- The CoffeeScript logo font is Googles Galada -->
<link href="https://fonts.googleapis.com/css?family=Alegreya+Sans:400,800|Lato:300,300i,400,700|Roboto+Mono:400,400i" rel="stylesheet" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/codemirror/4.5.0/codemirror.css" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/codemirror@5.37.0/lib/codemirror.css" rel="stylesheet" crossorigin="anonymous">
<style>
<%= include('docs.css') %>
<%= include('code.css') %>
<%= include('docs.css') %>
</style>

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// This **Browser** compatibility layer extends core CoffeeScript functions
// to make things work smoothly when compiling code directly in the browser.
@@ -77,7 +77,7 @@
// all script tags with a content-type of `text/coffeescript`.
// This happens on page load.
runScripts = function() {
var coffees, coffeetypes, execute, fn, i, index, j, len, s, script, scripts;
var coffees, coffeetypes, execute, i, index, j, len, s, script, scripts;
scripts = window.document.getElementsByTagName('script');
coffeetypes = ['text/coffeescript', 'text/literate-coffeescript'];
coffees = (function() {
@@ -101,32 +101,31 @@
return execute();
}
};
fn = function(script, i) {
var options, source;
options = {
literate: script.type === coffeetypes[1]
};
source = script.src || script.getAttribute('data-src');
if (source) {
options.filename = source;
return CoffeeScript.load(source, function(param) {
coffees[i] = param;
return execute();
}, options, true);
} else {
// `options.filename` defines the filename the source map appears as
// in Developer Tools. If a script tag has an `id`, use that as the
// filename; otherwise use `coffeescript`, or `coffeescript1` etc.,
// leaving the first one unnumbered for the common case that theres
// only one CoffeeScript script block to parse.
options.filename = script.id && script.id !== '' ? script.id : `coffeescript${(i !== 0 ? i : '')}`;
options.sourceFiles = ['embedded'];
return coffees[i] = [script.innerHTML, options];
}
};
for (i = j = 0, len = coffees.length; j < len; i = ++j) {
script = coffees[i];
fn(script, i);
(function(script, i) {
var options, source;
options = {
literate: script.type === coffeetypes[1]
};
source = script.src || script.getAttribute('data-src');
if (source) {
options.filename = source;
return CoffeeScript.load(source, function(param) {
coffees[i] = param;
return execute();
}, options, true);
} else {
// `options.filename` defines the filename the source map appears as
// in Developer Tools. If a script tag has an `id`, use that as the
// filename; otherwise use `coffeescript`, or `coffeescript1` etc.,
// leaving the first one unnumbered for the common case that theres
// only one CoffeeScript script block to parse.
options.filename = script.id && script.id !== '' ? script.id : `coffeescript${(i !== 0 ? i : '')}`;
options.sourceFiles = ['embedded'];
return coffees[i] = [script.innerHTML, options];
}
})(script, i);
}
return execute();
};

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// `cake` is a simplified version of [Make](http://www.gnu.org/software/make/)
// ([Rake](http://rake.rubyforge.org/), [Jake](https://github.com/280north/jake))

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// CoffeeScript can be used both on the server, as a command-line compiler based
// on Node.js/V8, or to run CoffeeScript directly in the browser. This module
@@ -85,7 +85,7 @@
// object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for
// doing programmatic lookups.
exports.compile = compile = withPrettyErrors(function(code, options = {}) {
var currentColumn, currentLine, encoded, filename, fragment, fragments, generateSourceMap, header, i, j, js, len, len1, map, newLines, ref, ref1, sourceMapDataURI, sourceURL, token, tokens, transpiler, transpilerOutput, v3SourceMap;
var currentColumn, currentLine, encoded, filename, fragment, fragments, generateSourceMap, header, i, j, js, len, len1, map, newLines, ref, ref1, sourceMapDataURI, sourceURL, token, tokens, transpiler, transpilerOptions, transpilerOutput, v3SourceMap;
// Clone `options`, to avoid mutating the `options` object passed in.
options = Object.assign({}, options);
// Always generate a source map if no filename is passed in, since without a
@@ -177,13 +177,14 @@
// is run via the CLI or Node API.
transpiler = options.transpile.transpile;
delete options.transpile.transpile;
transpilerOptions = Object.assign({}, options.transpile);
// See https://github.com/babel/babel/issues/827#issuecomment-77573107:
// Babel can take a v3 source map object as input in `inputSourceMap`
// and it will return an *updated* v3 source map object in its output.
if (v3SourceMap && (options.transpile.inputSourceMap == null)) {
options.transpile.inputSourceMap = v3SourceMap;
if (v3SourceMap && (transpilerOptions.inputSourceMap == null)) {
transpilerOptions.inputSourceMap = v3SourceMap;
}
transpilerOutput = transpiler(js, options.transpile);
transpilerOutput = transpiler(js, transpilerOptions);
js = transpilerOutput.code;
if (v3SourceMap && transpilerOutput.map) {
v3SourceMap = transpilerOutput.map;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// The `coffee` utility. Handles command-line compilation of CoffeeScript
// into various forms: saved into `.js` files or printed to stdout
@@ -534,7 +534,7 @@
// same directory as the `.js` file.
writeJs = function(base, sourcePath, js, jsPath, generatedSourceMap = null) {
var compile, jsDir, sourceMapPath;
sourceMapPath = outputPath(sourcePath, base, ".js.map");
sourceMapPath = `${jsPath}.map`;
jsDir = path.dirname(jsPath);
compile = function() {
if (opts.compile) {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// The CoffeeScript parser is generated by [Jison](https://github.com/zaach/jison)
// from this grammar file. Jison is a bottom-up parser generator, similar in
@@ -97,7 +97,7 @@
// Block and statements, which make up a line in a body. YieldReturn is a
// statement, but not included in Statement because that results in an ambiguous
// grammar.
Line: [o('Expression'), o('Statement'), o('FuncDirective')],
Line: [o('Expression'), o('ExpressionLine'), o('Statement'), o('FuncDirective')],
FuncDirective: [o('YieldReturn'), o('AwaitReturn')],
// Pure statements which cannot be expressions.
Statement: [
@@ -114,6 +114,10 @@
// is one. Blocks serve as the building blocks of many other rules, making
// them somewhat circular.
Expression: [o('Value'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw'), o('Yield')],
// Expressions which are written in single line and would otherwise require being
// wrapped in braces: E.g `a = b if do -> f a is 1`, `if f (a) -> a*2 then ...`,
// `for x in do (obj) -> f obj when x > 8 then f x`
ExpressionLine: [o('CodeLine'), o('IfLine'), o('OperationLine')],
Yield: [
o('YIELD',
function() {
@@ -283,7 +287,14 @@
})
],
SimpleObjAssignable: [o('Identifier'), o('Property'), o('ThisProperty')],
ObjAssignable: [o('SimpleObjAssignable'), o('AlphaNumeric')],
ObjAssignable: [
o('SimpleObjAssignable'),
o('[ Expression ]',
function() {
return new Value(new ComputedPropertyName($2));
}),
o('AlphaNumeric')
],
// Object literal spread properties.
ObjRestValue: [
o('SimpleObjAssignable ...',
@@ -399,6 +410,22 @@
$1);
})
],
// The Codeline is the **Code** node with **Line** instead of indented **Block**.
CodeLine: [
o('PARAM_START ParamList PARAM_END FuncGlyph Line',
function() {
return new Code($2,
LOC(5)(Block.wrap([$5])),
$4,
LOC(1)(new Literal($1)));
}),
o('FuncGlyph Line',
function() {
return new Code([],
LOC(2)(Block.wrap([$2])),
$1);
})
],
// CoffeeScript has two different symbols for functions. `->` is for ordinary
// functions, and `=>` is for functions bound to the current value of *this*.
FuncGlyph: [
@@ -822,6 +849,10 @@
function() {
return new ExportDefaultDeclaration($3);
}),
o('EXPORT DEFAULT INDENT Object OUTDENT',
function() {
return new ExportDefaultDeclaration(new Value($4));
}),
o('EXPORT EXPORT_ALL FROM String',
function() {
return new ExportAllDeclaration(new Literal($2),
@@ -950,9 +981,14 @@
function() {
return new Arr([]);
}),
o('[ ArgList OptComma ]',
o('[ Elisions ]',
function() {
return new Arr($2);
}),
o('[ ArgElisionList OptElisions ]',
function() {
return new Arr([].concat($2,
$3));
})
],
// Inclusive and exclusive range dots.
@@ -973,6 +1009,12 @@
return new Range($2,
$4,
$3);
}),
o('[ ExpressionLine RangeDots Expression ]',
function() {
return new Range($2,
$4,
$3);
})
],
// Array slice literals.
@@ -989,6 +1031,18 @@
null,
$2);
}),
o('ExpressionLine RangeDots Expression',
function() {
return new Range($1,
$3,
$2);
}),
o('ExpressionLine RangeDots',
function() {
return new Range($1,
null,
$2);
}),
o('RangeDots Expression',
function() {
return new Range(null,
@@ -1002,8 +1056,7 @@
$1);
})
],
// The **ArgList** is both the list of objects passed into a function call,
// as well as the contents of an array literal
// The **ArgList** is the list of objects passed into a function call
// (i.e. comma-separated expressions). Newlines work as well.
ArgList: [
o('Arg',
@@ -1030,21 +1083,88 @@
// Valid arguments are Blocks or Splats.
Arg: [
o('Expression'),
o('ExpressionLine'),
o('Splat'),
o('...',
function() {
return new Expansion;
})
],
// The **ArgElisionList** is the list of objects, contents of an array literal
// (i.e. comma-separated expressions and elisions). Newlines work as well.
ArgElisionList: [
o('ArgElision'),
o('ArgElisionList , ArgElision',
function() {
return $1.concat($3);
}),
o('ArgElisionList OptElisions TERMINATOR ArgElision',
function() {
return $1.concat($2,
$4);
}),
o('INDENT ArgElisionList OptElisions OUTDENT',
function() {
return $2.concat($3);
}),
o('ArgElisionList OptElisions INDENT ArgElisionList OptElisions OUTDENT',
function() {
return $1.concat($2,
$4,
$5);
})
],
ArgElision: [
o('Arg',
function() {
return [$1];
}),
o('Elisions Arg',
function() {
return $1.concat($2);
})
],
OptElisions: [
o('OptComma',
function() {
return [];
}),
o(', Elisions',
function() {
return [].concat($2);
})
],
Elisions: [
o('Elision',
function() {
return [$1];
}),
o('Elisions Elision',
function() {
return $1.concat($2);
})
],
Elision: [
o(',',
function() {
return new Elision;
})
],
// Just simple, comma-separated, required arguments (no fancy syntax). We need
// this to be separate from the **ArgList** for use in **Switch** blocks, where
// having the newlines wouldn't make sense.
SimpleArgs: [
o('Expression'),
o('ExpressionLine'),
o('SimpleArgs , Expression',
function() {
return [].concat($1,
$3);
}),
o('SimpleArgs , ExpressionLine',
function() {
return [].concat($1,
$3);
})
],
// The variants of *try/catch/finally* exception handling blocks.
@@ -1118,6 +1238,34 @@
})
],
// The condition portion of a while loop.
WhileLineSource: [
o('WHILE ExpressionLine',
function() {
return new While($2);
}),
o('WHILE ExpressionLine WHEN ExpressionLine',
function() {
return new While($2,
{
guard: $4
});
}),
o('UNTIL ExpressionLine',
function() {
return new While($2,
{
invert: true
});
}),
o('UNTIL ExpressionLine WHEN ExpressionLine',
function() {
return new While($2,
{
invert: true,
guard: $4
});
})
],
WhileSource: [
o('WHILE Expression',
function() {
@@ -1130,6 +1278,13 @@
guard: $4
});
}),
o('WHILE ExpressionLine WHEN Expression',
function() {
return new While($2,
{
guard: $4
});
}),
o('UNTIL Expression',
function() {
return new While($2,
@@ -1138,6 +1293,14 @@
});
}),
o('UNTIL Expression WHEN Expression',
function() {
return new While($2,
{
invert: true,
guard: $4
});
}),
o('UNTIL ExpressionLine WHEN Expression',
function() {
return new While($2,
{
@@ -1153,6 +1316,10 @@
function() {
return $1.addBody($2);
}),
o('WhileLineSource Block',
function() {
return $1.addBody($2);
}),
o('Statement WhileSource',
function() {
return $2.addBody(LOC(1)(Block.wrap([$1])));
@@ -1180,55 +1347,94 @@
// Comprehensions can either be normal, with a block of expressions to execute,
// or postfix, with a single expression.
For: [
o('Statement ForBody',
o('Statement ForBody',
function() {
return new For($1,
$2);
return $2.addBody($1);
}),
o('Expression ForBody',
o('Expression ForBody',
function() {
return new For($1,
$2);
return $2.addBody($1);
}),
o('ForBody Block',
o('ForBody Block',
function() {
return new For($2,
$1);
return $1.addBody($2);
}),
o('ForLineBody Block',
function() {
return $1.addBody($2);
})
],
ForBody: [
o('FOR Range',
function() {
return {
return new For([],
{
source: LOC(2)(new Value($2))
};
});
}),
o('FOR Range BY Expression',
function() {
return {
return new For([],
{
source: LOC(2)(new Value($2)),
step: $4
};
});
}),
o('ForStart ForSource',
function() {
$2.own = $1.own;
$2.ownTag = $1.ownTag;
$2.name = $1[0];
$2.index = $1[1];
return $2;
return $1.addSource($2);
})
],
ForLineBody: [
o('FOR Range BY ExpressionLine',
function() {
return new For([],
{
source: LOC(2)(new Value($2)),
step: $4
});
}),
o('ForStart ForLineSource',
function() {
return $1.addSource($2);
})
],
ForStart: [
o('FOR ForVariables',
function() {
return $2;
return new For([],
{
name: $2[0],
index: $2[1]
});
}),
o('FOR AWAIT ForVariables',
function() {
var index,
name;
[name,
index] = $3;
return new For([],
{
name,
index,
await: true,
awaitTag: LOC(2)(new Literal($2))
});
}),
o('FOR OWN ForVariables',
function() {
$3.own = true;
$3.ownTag = LOC(2)(new Literal($2));
return $3;
var index,
name;
[name,
index] = $3;
return new For([],
{
name,
index,
own: true,
ownTag: LOC(2)(new Literal($2))
});
})
],
// An array of all accepted values for a variable inside the loop.
@@ -1283,6 +1489,13 @@
guard: $4
};
}),
o('FORIN ExpressionLine WHEN Expression',
function() {
return {
source: $2,
guard: $4
};
}),
o('FOROF Expression WHEN Expression',
function() {
return {
@@ -1291,6 +1504,14 @@
object: true
};
}),
o('FOROF ExpressionLine WHEN Expression',
function() {
return {
source: $2,
guard: $4,
object: true
};
}),
o('FORIN Expression BY Expression',
function() {
return {
@@ -1298,6 +1519,13 @@
step: $4
};
}),
o('FORIN ExpressionLine BY Expression',
function() {
return {
source: $2,
step: $4
};
}),
o('FORIN Expression WHEN Expression BY Expression',
function() {
return {
@@ -1306,6 +1534,30 @@
step: $6
};
}),
o('FORIN ExpressionLine WHEN Expression BY Expression',
function() {
return {
source: $2,
guard: $4,
step: $6
};
}),
o('FORIN Expression WHEN ExpressionLine BY Expression',
function() {
return {
source: $2,
guard: $4,
step: $6
};
}),
o('FORIN ExpressionLine WHEN ExpressionLine BY Expression',
function() {
return {
source: $2,
guard: $4,
step: $6
};
}),
o('FORIN Expression BY Expression WHEN Expression',
function() {
return {
@@ -1314,6 +1566,30 @@
guard: $6
};
}),
o('FORIN ExpressionLine BY Expression WHEN Expression',
function() {
return {
source: $2,
step: $4,
guard: $6
};
}),
o('FORIN Expression BY ExpressionLine WHEN Expression',
function() {
return {
source: $2,
step: $4,
guard: $6
};
}),
o('FORIN ExpressionLine BY ExpressionLine WHEN Expression',
function() {
return {
source: $2,
step: $4,
guard: $6
};
}),
o('FORFROM Expression',
function() {
return {
@@ -1322,6 +1598,160 @@
};
}),
o('FORFROM Expression WHEN Expression',
function() {
return {
source: $2,
guard: $4,
from: true
};
}),
o('FORFROM ExpressionLine WHEN Expression',
function() {
return {
source: $2,
guard: $4,
from: true
};
})
],
ForLineSource: [
o('FORIN ExpressionLine',
function() {
return {
source: $2
};
}),
o('FOROF ExpressionLine',
function() {
return {
source: $2,
object: true
};
}),
o('FORIN Expression WHEN ExpressionLine',
function() {
return {
source: $2,
guard: $4
};
}),
o('FORIN ExpressionLine WHEN ExpressionLine',
function() {
return {
source: $2,
guard: $4
};
}),
o('FOROF Expression WHEN ExpressionLine',
function() {
return {
source: $2,
guard: $4,
object: true
};
}),
o('FOROF ExpressionLine WHEN ExpressionLine',
function() {
return {
source: $2,
guard: $4,
object: true
};
}),
o('FORIN Expression BY ExpressionLine',
function() {
return {
source: $2,
step: $4
};
}),
o('FORIN ExpressionLine BY ExpressionLine',
function() {
return {
source: $2,
step: $4
};
}),
o('FORIN Expression WHEN Expression BY ExpressionLine',
function() {
return {
source: $2,
guard: $4,
step: $6
};
}),
o('FORIN ExpressionLine WHEN Expression BY ExpressionLine',
function() {
return {
source: $2,
guard: $4,
step: $6
};
}),
o('FORIN Expression WHEN ExpressionLine BY ExpressionLine',
function() {
return {
source: $2,
guard: $4,
step: $6
};
}),
o('FORIN ExpressionLine WHEN ExpressionLine BY ExpressionLine',
function() {
return {
source: $2,
guard: $4,
step: $6
};
}),
o('FORIN Expression BY Expression WHEN ExpressionLine',
function() {
return {
source: $2,
step: $4,
guard: $6
};
}),
o('FORIN ExpressionLine BY Expression WHEN ExpressionLine',
function() {
return {
source: $2,
step: $4,
guard: $6
};
}),
o('FORIN Expression BY ExpressionLine WHEN ExpressionLine',
function() {
return {
source: $2,
step: $4,
guard: $6
};
}),
o('FORIN ExpressionLine BY ExpressionLine WHEN ExpressionLine',
function() {
return {
source: $2,
step: $4,
guard: $6
};
}),
o('FORFROM ExpressionLine',
function() {
return {
source: $2,
from: true
};
}),
o('FORFROM Expression WHEN ExpressionLine',
function() {
return {
source: $2,
guard: $4,
from: true
};
}),
o('FORFROM ExpressionLine WHEN ExpressionLine',
function() {
return {
source: $2,
@@ -1336,12 +1766,23 @@
return new Switch($2,
$4);
}),
o('SWITCH ExpressionLine INDENT Whens OUTDENT',
function() {
return new Switch($2,
$4);
}),
o('SWITCH Expression INDENT Whens ELSE Block OUTDENT',
function() {
return new Switch($2,
$4,
$6);
}),
o('SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT',
function() {
return new Switch($2,
$4,
$6);
}),
o('SWITCH INDENT Whens OUTDENT',
function() {
return new Switch(null,
@@ -1423,12 +1864,63 @@
});
})
],
IfBlockLine: [
o('IF ExpressionLine Block',
function() {
return new If($2,
$3,
{
type: $1
});
}),
o('IfBlockLine ELSE IF ExpressionLine Block',
function() {
return $1.addElse(LOC(3,
5)(new If($4,
$5,
{
type: $3
})));
})
],
IfLine: [
o('IfBlockLine'),
o('IfBlockLine ELSE Block',
function() {
return $1.addElse($3);
}),
o('Statement POST_IF ExpressionLine',
function() {
return new If($3,
LOC(1)(Block.wrap([$1])),
{
type: $2,
statement: true
});
}),
o('Expression POST_IF ExpressionLine',
function() {
return new If($3,
LOC(1)(Block.wrap([$1])),
{
type: $2,
statement: true
});
})
],
// Arithmetic and logical operators, working on one or more operands.
// Here they are grouped by order of precedence. The actual precedence rules
// are defined at the bottom of the page. It would be shorter if we could
// combine most of these rules into a single generic *Operand OpSymbol Operand*
// -type rule, but in order to make the precedence binding possible, separate
// rules are necessary.
OperationLine: [
o('UNARY ExpressionLine',
function() {
return new Op($1,
$2);
})
],
Operation: [
o('UNARY Expression',
function() {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// This file contains the common helper functions that we'd like to share among
// the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten

View File

@@ -1,7 +1,7 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// Node.js Implementation
var CoffeeScript, ext, fn, fs, helpers, i, len, path, ref, universalCompile, vm,
var CoffeeScript, ext, fs, helpers, i, len, path, ref, universalCompile, vm,
hasProp = {}.hasOwnProperty;
CoffeeScript = require('./coffeescript');
@@ -137,15 +137,14 @@
// Throw error with deprecation warning when depending upon implicit `require.extensions` registration
if (require.extensions) {
ref = CoffeeScript.FILE_EXTENSIONS;
fn = function(ext) {
var base;
return (base = require.extensions)[ext] != null ? base[ext] : base[ext] = function() {
throw new Error(`Use CoffeeScript.register() or require the coffeescript/register module to require ${ext} files.`);
};
};
for (i = 0, len = ref.length; i < len; i++) {
ext = ref[i];
fn(ext);
(function(ext) {
var base;
return (base = require.extensions)[ext] != null ? base[ext] : base[ext] = function() {
throw new Error(`Use CoffeeScript.register() or require the coffeescript/register module to require ${ext} files.`);
};
})(ext);
}
}

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
// matches against the beginning of the source code. When a match is found,
@@ -10,8 +10,9 @@
// where locationData is {first_line, first_column, last_line, last_column}, which is a
// format that can be fed directly into [Jison](https://github.com/zaach/jison). These
// are read by jison in the `parser.lexer` function defined in coffeescript.coffee.
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, UNFINISHED, UNICODE_CODE_POINT_ESCAPE, VALID_FLAGS, WHITESPACE, attachCommentsToNode, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, merge, repeat, starts, throwSyntaxError,
indexOf = [].indexOf;
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARABLE_LEFT_SIDE, COMPARE, COMPOUND_ASSIGN, CSX_ATTRIBUTE, CSX_FRAGMENT_IDENTIFIER, 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, UNFINISHED, UNICODE_CODE_POINT_ESCAPE, VALID_FLAGS, WHITESPACE, attachCommentsToNode, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, merge, repeat, starts, throwSyntaxError,
indexOf = [].indexOf,
slice = [].slice;
({Rewriter, INVERSES} = require('./rewriter'));
@@ -111,7 +112,7 @@
// referenced as property names here, so you can still do `jQuery.is()` even
// though `is` means `===` otherwise.
identifierToken() {
var alias, colon, colonOffset, colonToken, id, idLength, inCSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, regExSuper, regex, sup, tag, tagToken;
var alias, colon, colonOffset, colonToken, id, idLength, inCSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref10, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, regExSuper, regex, sup, tag, tagToken;
inCSXTag = this.atCSXTag();
regex = inCSXTag ? CSX_ATTRIBUTE : IDENTIFIER;
if (!(match = regex.exec(this.chunk))) {
@@ -199,11 +200,11 @@
// what CoffeeScript would normally interpret as calls to functions named
// `get` or `set`, i.e. `get({foo: function () {}})`.
} else if (tag === 'PROPERTY' && prev) {
if (prev.spaced && (ref7 = prev[0], indexOf.call(CALLABLE, ref7) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens[this.tokens.length - 2][0] !== '.') {
if (prev.spaced && (ref7 = prev[0], indexOf.call(CALLABLE, ref7) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens.length > 1 && ((ref8 = this.tokens[this.tokens.length - 2][0]) !== '.' && ref8 !== '?.' && ref8 !== '@')) {
this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
} else {
} else if (this.tokens.length > 2) {
prevprev = this.tokens[this.tokens.length - 2];
if (((ref8 = prev[0]) === '@' || ref8 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && this.tokens[this.tokens.length - 3][0] !== '.') {
if (((ref9 = prev[0]) === '@' || ref9 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && ((ref10 = this.tokens[this.tokens.length - 3][0]) !== '.' && ref10 !== '?.' && ref10 !== '@')) {
this.error(`'${prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prevprev[2]);
}
}
@@ -586,16 +587,20 @@
// Keeps track of the level of indentation, because a single outdent token
// can close multiple indents, so we need to know how far in we happen to be.
lineToken(chunk = this.chunk) {
var diff, indent, match, minLiteralLength, newIndentLiteral, noNewlines, size;
var backslash, diff, indent, match, minLiteralLength, newIndentLiteral, noNewlines, prev, size;
if (!(match = MULTI_DENT.exec(chunk))) {
return 0;
}
indent = match[0];
this.seenFor = false;
if (!this.importSpecifierList) {
prev = this.prev();
backslash = (prev != null ? prev[0] : void 0) === '\\';
if (!(backslash && this.seenFor)) {
this.seenFor = false;
}
if (!((backslash && this.seenImport) || this.importSpecifierList)) {
this.seenImport = false;
}
if (!this.exportSpecifierList) {
if (!((backslash && this.seenExport) || this.exportSpecifierList)) {
this.seenExport = false;
}
size = indent.length - 1 - indent.lastIndexOf('\n');
@@ -624,7 +629,9 @@
}
if (size > this.indent) {
if (noNewlines) {
this.indebt = size - this.indent;
if (!backslash) {
this.indebt = size - this.indent;
}
this.suppressNewlines();
return indent.length;
}
@@ -741,7 +748,7 @@
// Check the previous token to detect if attribute is spread.
prevChar = this.tokens.length > 0 ? this.tokens[this.tokens.length - 1][0] : '';
if (firstChar === '<') {
match = CSX_IDENTIFIER.exec(this.chunk.slice(1));
match = CSX_IDENTIFIER.exec(this.chunk.slice(1)) || CSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(1));
// Not the right hand side of an unspaced comparison (i.e. `a<b`).
if (!(match && (this.csxDepth > 0 || !(prev = this.prev()) || prev.spaced || (ref = prev[0], indexOf.call(COMPARABLE_LEFT_SIDE, ref) < 0)))) {
return 0;
@@ -793,8 +800,8 @@
delimiter: '>'
});
});
match = CSX_IDENTIFIER.exec(this.chunk.slice(end));
if (!match || match[0] !== csxTag.name) {
match = CSX_IDENTIFIER.exec(this.chunk.slice(end)) || CSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(end));
if (!match || match[1] !== csxTag.name) {
this.error(`expected corresponding CSX closing tag for ${csxTag.name}`, csxTag.origin[2]);
}
afterTag = end + csxTag.name.length;
@@ -1050,7 +1057,7 @@
if (braceInterpolator) {
// Turn the leading and trailing `{` and `}` into parentheses. Unnecessary
// parentheses will be removed later.
open = nested[0], close = nested[nested.length - 1];
[open] = nested, [close] = slice.call(nested, -1);
open[0] = open[1] = '(';
close[0] = close[1] = ')';
close.origin = ['', 'end of interpolation', close[2]];
@@ -1075,7 +1082,7 @@
length: delimiter.length
});
}
firstToken = tokens[0], lastToken = tokens[tokens.length - 1];
[firstToken] = tokens, [lastToken] = slice.call(tokens, -1);
firstToken[2].first_column -= delimiter.length;
if (lastToken[1].substr(-1) === '\n') {
lastToken[2].last_line += 1;
@@ -1176,7 +1183,7 @@
this.tokens.push(...tokensToPush);
}
if (lparen) {
lastToken = tokens[tokens.length - 1];
[lastToken] = slice.call(tokens, -1);
lparen.origin = [
'STRING',
null,
@@ -1202,7 +1209,7 @@
// correctly balanced throughout the course of the token stream.
pair(tag) {
var lastIndent, prev, ref, ref1, wanted;
ref = this.ends, prev = ref[ref.length - 1];
ref = this.ends, [prev] = slice.call(ref, -1);
if (tag !== (wanted = prev != null ? prev.tag : void 0)) {
if ('OUTDENT' !== wanted) {
this.error(`unmatched ${tag}`);
@@ -1212,7 +1219,7 @@
// el.click((event) ->
// el.hide())
ref1 = this.indents, lastIndent = ref1[ref1.length - 1];
ref1 = this.indents, [lastIndent] = slice.call(ref1, -1);
this.outdentToken(lastIndent, true);
return this.pair(tag);
}
@@ -1238,7 +1245,7 @@
lineCount = count(string, '\n');
column = this.chunkColumn;
if (lineCount > 0) {
ref = string.split('\n'), lastLine = ref[ref.length - 1];
ref = string.split('\n'), [lastLine] = slice.call(ref, -1);
column = lastLine.length;
} else {
column += string.length;
@@ -1279,14 +1286,14 @@
// Peek at the last tag in the token stream.
tag() {
var ref, token;
ref = this.tokens, token = ref[ref.length - 1];
ref = this.tokens, [token] = slice.call(ref, -1);
return token != null ? token[0] : void 0;
}
// Peek at the last value in the token stream.
value(useOrigin = false) {
var ref, ref1, token;
ref = this.tokens, token = ref[ref.length - 1];
ref = this.tokens, [token] = slice.call(ref, -1);
if (useOrigin && ((token != null ? token.origin : void 0) != null)) {
return (ref1 = token.origin) != null ? ref1[1] : void 0;
} else {
@@ -1541,6 +1548,9 @@
CSX_IDENTIFIER = /^(?![\d<])((?:(?!\s)[\.\-$\w\x7f-\uffff])+)/; // Must not start with `<`.
// Like `IDENTIFIER`, but includes `-`s and `.`s.
// Fragment: <></>
CSX_FRAGMENT_IDENTIFIER = /^()>/; // Ends immediately with `>`.
CSX_ATTRIBUTE = /^(?!\d)((?:(?!\s)[\-$\w\x7f-\uffff])+)([^\S]*=(?!=))?/; // Like `IDENTIFIER`, but includes `-`s.
// Is this an attribute with a value?
@@ -1600,9 +1610,14 @@
REGEX_FLAGS = /^\w*/;
VALID_FLAGS = /^(?!.*(.).*\1)[imguy]*$/;
VALID_FLAGS = /^(?!.*(.).*\1)[gimsuy]*$/;
HEREGEX = /^(?:[^\\\/#]|\\[\s\S]|\/(?!\/\/)|\#(?!\{))*/;
// Match any character, except those that need special handling below.
// Match `\` followed by any character.
// Match any `/` except `///`.
// Match `#` which is not part of interpolation, e.g. `#{}`.
// Comments consume everything until the end of the line, including `///`.
HEREGEX = /^(?:[^\\\/#\s]|\\[\s\S]|\/(?!\/\/)|\#(?!\{)|\s+(?:#(?!\{).*)?)*/;
HEREGEX_OMIT = /((?:\\\\)+)|\\(\s)|\s+(?:#.*)?/g; // Consume (and preserve) an even number of backslashes.
// Preserve escaped whitespace.
@@ -1685,6 +1700,6 @@
INDENTABLE_CLOSERS = [')', '}', ']'];
// Tokens that, when appearing at the end of a line, suppress a following TERMINATOR/INDENT token
UNFINISHED = ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-', '**', 'SHIFT', 'RELATION', 'COMPARE', '&', '^', '|', '&&', '||', 'BIN?', 'EXTENDS', 'DEFAULT'];
UNFINISHED = ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-', '**', 'SHIFT', 'RELATION', 'COMPARE', '&', '^', '|', '&&', '||', 'BIN?', 'EXTENDS'];
}).call(this);

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat,
slice = [].slice;
splice = [].splice;
({repeat} = require('./helpers'));
@@ -44,8 +44,8 @@
// `--` argument after, because that will fail on Linux (see #3946).
({rules, positional} = normalizeArguments(args, this.rules.flagDict));
options = {};
// The `argument` field is added to the rule instance non-destructively by
// `normalizeArguments`.
// The `argument` field is added to the rule instance non-destructively by
// `normalizeArguments`.
for (i = 0, len = rules.length; i < len; i++) {
({hasArgument, argument, isList, name} = rules[i]);
if (hasArgument) {
@@ -157,7 +157,7 @@
};
normalizeArguments = function(args, flagDict) {
var arg, argIndex, flag, i, innerOpts, j, k, lastOpt, len, len1, multiFlags, multiOpts, needsArgOpt, positional, ref, rule, rules, singleRule, withArg;
var arg, argIndex, flag, i, innerOpts, j, lastOpt, len, len1, multiFlags, multiOpts, needsArgOpt, positional, ref, rule, rules, singleRule, withArg;
rules = [];
positional = [];
needsArgOpt = null;
@@ -187,9 +187,9 @@
return {rule, flag};
});
// Only the last flag in a multi-flag may have an argument.
innerOpts = 2 <= multiOpts.length ? slice.call(multiOpts, 0, j = multiOpts.length - 1) : (j = 0, []), lastOpt = multiOpts[j++];
for (k = 0, len1 = innerOpts.length; k < len1; k++) {
({rule, flag} = innerOpts[k]);
[...innerOpts] = multiOpts, [lastOpt] = splice.call(innerOpts, -1);
for (j = 0, len1 = innerOpts.length; j < len1; j++) {
({rule, flag} = innerOpts[j]);
if (rule.hasArgument) {
throw new Error(`cannot use option ${flag} in multi-flag ${arg} except as the last option, because it needs an argument`);
}

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
var CoffeeScript, Module, binary, child_process, ext, findExtension, fork, getRootModule, helpers, i, len, loadFile, path, ref;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
var CoffeeScript, addHistory, addMultilineHandler, fs, getCommandId, merge, nodeREPL, path, replDefaults, runInContext, sawSIGINT, transpile, updateSyntaxError, vm;
@@ -29,7 +29,7 @@
})(),
historyMaxInputSize: 10240,
eval: function(input, context, filename, cb) {
var Assign, Block, Call, Code, Literal, Value, ast, err, isAsync, js, referencedVars, result, token, tokens;
var Assign, Block, Call, Code, Literal, Value, ast, err, isAsync, js, ref, ref1, referencedVars, result, token, tokens;
// XXX: multiline hack.
input = input.replace(/\uFF00/g, '\n');
// Node's REPL sends the input ending with a newline and then wrapped in
@@ -43,6 +43,13 @@
try {
// Tokenize the clean input.
tokens = CoffeeScript.tokens(input);
// Filter out tokens generated just to hold comments.
if (tokens.length >= 2 && tokens[0].generated && ((ref = tokens[0].comments) != null ? ref.length : void 0) !== 0 && tokens[0][1] === '' && tokens[1][0] === 'TERMINATOR') {
tokens = tokens.slice(2);
}
if (tokens.length >= 1 && tokens[tokens.length - 1].generated && ((ref1 = tokens[tokens.length - 1].comments) != null ? ref1.length : void 0) !== 0 && tokens[tokens.length - 1][1] === '') {
tokens.pop();
}
// Collect referenced variable names just like in `CoffeeScript.compile`.
referencedVars = (function() {
var i, len, results;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// The CoffeeScript language has a good deal of optional syntax, implicit syntax,
// and shorthand syntax. This can greatly complicate a grammar and bloat
@@ -218,7 +218,7 @@
indexOfTag(i, ...pattern) {
var fuzz, j, k, ref, ref1;
fuzz = 0;
for (j = k = 0, ref = pattern.length; 0 <= ref ? k < ref : k > ref; j = 0 <= ref ? ++k : --k) {
for (j = k = 0, ref = pattern.length; (0 <= ref ? k < ref : k > ref); j = 0 <= ref ? ++k : --k) {
if (pattern[j] == null) {
continue;
}
@@ -281,7 +281,7 @@
stack = [];
start = null;
return this.scanTokens(function(token, i, tokens) {
var endImplicitCall, endImplicitObject, forward, implicitObjectContinues, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, isImplicit, isImplicitCall, isImplicitObject, k, newLine, nextTag, nextToken, offset, prevTag, prevToken, ref, ref1, s, sameLine, stackIdx, stackItem, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag;
var endImplicitCall, endImplicitObject, forward, implicitObjectContinues, inControlFlow, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, isImplicit, isImplicitCall, isImplicitObject, k, newLine, nextTag, nextToken, offset, prevTag, prevToken, ref, ref1, ref2, s, sameLine, stackIdx, stackItem, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag;
[tag] = token;
[prevTag] = prevToken = i > 0 ? tokens[i - 1] : [];
[nextTag] = nextToken = i < tokens.length - 1 ? tokens[i + 1] : [];
@@ -420,10 +420,31 @@
}
start = stack.pop();
}
inControlFlow = () => {
var controlFlow, isFunc, seenFor, tagCurrentLine;
seenFor = this.findTagsBackwards(i, ['FOR']) && this.findTagsBackwards(i, ['FORIN', 'FOROF', 'FORFROM']);
controlFlow = seenFor || this.findTagsBackwards(i, ['WHILE', 'UNTIL', 'LOOP', 'LEADING_WHEN']);
if (!controlFlow) {
return false;
}
isFunc = false;
tagCurrentLine = token[2].first_line;
this.detectEnd(i, function(token, i) {
var ref;
return ref = token[0], indexOf.call(LINEBREAKS, ref) >= 0;
}, function(token, i) {
var first_line;
[prevTag, , {first_line}] = tokens[i - 1] || [];
return isFunc = tagCurrentLine === first_line && (prevTag === '->' || prevTag === '=>');
}, {
returnOnNegativeLevel: true
});
return isFunc;
};
// Recognize standard implicit calls like
// f a, f() b, f? c, h[0] d etc.
// Added support for spread dots on the left side: f ...a
if ((indexOf.call(IMPLICIT_FUNC, tag) >= 0 && token.spaced || tag === '?' && i > 0 && !tokens[i - 1].spaced) && (indexOf.call(IMPLICIT_CALL, nextTag) >= 0 || (nextTag === '...' && (ref = this.tag(i + 2), indexOf.call(IMPLICIT_CALL, ref) >= 0) && !this.findTagsBackwards(i, ['INDEX_START', '['])) || indexOf.call(IMPLICIT_UNSPACED_CALL, nextTag) >= 0 && !nextToken.spaced && !nextToken.newLine)) {
if ((indexOf.call(IMPLICIT_FUNC, tag) >= 0 && token.spaced || tag === '?' && i > 0 && !tokens[i - 1].spaced) && (indexOf.call(IMPLICIT_CALL, nextTag) >= 0 || (nextTag === '...' && (ref = this.tag(i + 2), indexOf.call(IMPLICIT_CALL, ref) >= 0) && !this.findTagsBackwards(i, ['INDEX_START', '['])) || indexOf.call(IMPLICIT_UNSPACED_CALL, nextTag) >= 0 && !nextToken.spaced && !nextToken.newLine) && !inControlFlow()) {
if (tag === '?') {
tag = token[0] = 'FUNC_EXIST';
}
@@ -506,11 +527,11 @@
}
}
newLine = prevTag === 'OUTDENT' || prevToken.newLine;
if (indexOf.call(IMPLICIT_END, tag) >= 0 || indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) {
if (indexOf.call(IMPLICIT_END, tag) >= 0 || (indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) || ((tag === '..' || tag === '...') && this.findTagsBackwards(i, ["INDEX_START"]))) {
while (inImplicit()) {
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop();
// Close implicit calls when reached end of argument list
if (inImplicitCall() && prevTag !== ',') {
if (inImplicitCall() && prevTag !== ',' || (prevTag === ',' && tag === 'TERMINATOR' && (nextTag == null))) {
endImplicitCall();
// Close implicit objects such as:
// return a: 1, b: 2 unless true
@@ -539,7 +560,7 @@
// f a, b: c, d: e, f, g: h: i, j
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && !((ref2 = this.tag(i + 2)) === 'FOROF' || ref2 === 'FORIN') && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
// When nextTag is OUTDENT the comma is insignificant and
// should just be ignored so embed it in the implicit object.
@@ -781,18 +802,51 @@
// newlines within expressions are removed and the indentation tokens of empty
// blocks are added.
normalizeLines() {
var action, condition, indent, outdent, starter;
var action, closeElseTag, condition, ifThens, indent, leading_if_then, leading_switch_when, outdent, starter;
starter = indent = outdent = null;
leading_switch_when = null;
leading_if_then = null;
// Count `THEN` tags
ifThens = [];
condition = function(token, i) {
var ref, ref1, ref2, ref3;
return token[1] !== ';' && (ref = token[0], indexOf.call(SINGLE_CLOSERS, ref) >= 0) && !(token[0] === 'TERMINATOR' && (ref1 = this.tag(i + 1), indexOf.call(EXPRESSION_CLOSE, ref1) >= 0)) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((ref2 = token[0]) === 'CATCH' || ref2 === 'FINALLY') && (starter === '->' || starter === '=>')) || (ref3 = token[0], indexOf.call(CALL_CLOSERS, ref3) >= 0) && (this.tokens[i - 1].newLine || this.tokens[i - 1][0] === 'OUTDENT');
return token[1] !== ';' && (ref = token[0], indexOf.call(SINGLE_CLOSERS, ref) >= 0) && !(token[0] === 'TERMINATOR' && (ref1 = this.tag(i + 1), indexOf.call(EXPRESSION_CLOSE, ref1) >= 0)) && !(token[0] === 'ELSE' && (starter !== 'THEN' || (leading_if_then || leading_switch_when))) && !(((ref2 = token[0]) === 'CATCH' || ref2 === 'FINALLY') && (starter === '->' || starter === '=>')) || (ref3 = token[0], indexOf.call(CALL_CLOSERS, ref3) >= 0) && (this.tokens[i - 1].newLine || this.tokens[i - 1][0] === 'OUTDENT');
};
action = function(token, i) {
if (token[0] === 'ELSE' && starter === 'THEN') {
ifThens.pop();
}
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
};
closeElseTag = (tokens, i) => {
var lastThen, outdentElse, tlen;
tlen = ifThens.length;
if (!(tlen > 0)) {
return i;
}
lastThen = ifThens.pop();
[, outdentElse] = this.indentation(tokens[lastThen]);
// Insert `OUTDENT` to close inner `IF`.
outdentElse[1] = tlen * 2;
tokens.splice(i, 0, outdentElse);
// Insert `OUTDENT` to close outer `IF`.
outdentElse[1] = 2;
tokens.splice(i + 1, 0, outdentElse);
// Remove outdents from the end.
this.detectEnd(i + 2, function(token, i) {
var ref;
return (ref = token[0]) === 'OUTDENT' || ref === 'TERMINATOR';
}, function(token, i) {
if (this.tag(i) === 'OUTDENT' && this.tag(i + 1) === 'OUTDENT') {
return tokens.splice(i, 2);
}
});
return i + 2;
};
return this.scanTokens(function(token, i, tokens) {
var j, k, ref, ref1, tag;
var conditionTag, j, k, ref, ref1, tag;
[tag] = token;
conditionTag = (tag === '->' || tag === '=>') && this.findTagsBackwards(i, ['IF', 'WHILE', 'FOR', 'UNTIL', 'SWITCH', 'WHEN', 'LEADING_WHEN', '[', 'INDEX_START']) && !(this.findTagsBackwards(i, ['THEN', '..', '...']));
if (tag === 'TERMINATOR') {
if (this.tag(i + 1) === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
tokens.splice(i, 1, ...this.indentation());
@@ -817,12 +871,23 @@
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')) {
if (indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF') && !conditionTag) {
starter = tag;
[indent, outdent] = this.indentation(tokens[i]);
if (starter === 'THEN') {
indent.fromThen = true;
}
if (tag === 'THEN') {
leading_switch_when = this.findTagsBackwards(i, ['LEADING_WHEN']) && this.tag(i + 1) === 'IF';
leading_if_then = this.findTagsBackwards(i, ['IF']) && this.tag(i + 1) === 'IF';
}
if (tag === 'THEN' && this.findTagsBackwards(i, ['IF'])) {
ifThens.push(i);
}
// `ELSE` tag is not closed.
if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
i = closeElseTag(tokens, i);
}
tokens.splice(i + 1, 0, indent);
this.detectEnd(i + 2, condition, action);
if (tag === 'THEN') {
@@ -886,7 +951,7 @@
return Rewriter;
})();
}).call(this);
// Constants
// ---------

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// The **Scope** class regulates lexical scoping within CoffeeScript. As you
// generate code, you create a tree of scopes in the same shape as the nested

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 2.0.2
// Generated by CoffeeScript 2.3.0
(function() {
// Source maps allow JavaScript runtimes to match running JavaScript back to
// the original source code that corresponds to it. This can be minified
@@ -203,7 +203,7 @@
return SourceMap;
})();
}).call(this);
// Our API for source maps is just the `SourceMap` class.
module.exports = SourceMap;

4315
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
"compiler"
],
"author": "Jeremy Ashkenas",
"version": "2.0.2",
"version": "2.3.0",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -39,17 +39,17 @@
"url": "git://github.com/jashkenas/coffeescript.git"
},
"devDependencies": {
"babel-core": "~6.26.0",
"babel-core": "~6.26.2",
"babel-preset-babili": "~0.1.4",
"babel-preset-env": "~1.6.0",
"babel-preset-minify": "^0.2.0",
"codemirror": "^5.29.0",
"docco": "~0.7.0",
"babel-preset-env": "~1.6.1",
"babel-preset-minify": "^0.4.0",
"codemirror": "^5.37.0",
"docco": "~0.8.0",
"highlight.js": "~9.12.0",
"jison": ">=0.4.18",
"markdown-it": "~8.4.0",
"underscore": "~1.8.3",
"webpack": "~3.6.0"
"markdown-it": "~8.4.1",
"underscore": "~1.9.0",
"webpack": "~4.6.0"
},
"dependencies": {}
}

View File

@@ -140,12 +140,14 @@ exports.compile = compile = withPrettyErrors (code, options = {}) ->
transpiler = options.transpile.transpile
delete options.transpile.transpile
transpilerOptions = Object.assign {}, options.transpile
# See https://github.com/babel/babel/issues/827#issuecomment-77573107:
# Babel can take a v3 source map object as input in `inputSourceMap`
# and it will return an *updated* v3 source map object in its output.
if v3SourceMap and not options.transpile.inputSourceMap?
options.transpile.inputSourceMap = v3SourceMap
transpilerOutput = transpiler js, options.transpile
if v3SourceMap and not transpilerOptions.inputSourceMap?
transpilerOptions.inputSourceMap = v3SourceMap
transpilerOutput = transpiler js, transpilerOptions
js = transpilerOutput.code
if v3SourceMap and transpilerOutput.map
v3SourceMap = transpilerOutput.map

View File

@@ -401,7 +401,7 @@ mkdirp = (dir, fn) ->
# If `generatedSourceMap` is provided, this will write a `.js.map` file into the
# same directory as the `.js` file.
writeJs = (base, sourcePath, js, jsPath, generatedSourceMap = null) ->
sourceMapPath = outputPath sourcePath, base, ".js.map"
sourceMapPath = "#{jsPath}.map"
jsDir = path.dirname jsPath
compile = ->
if opts.compile

View File

@@ -89,6 +89,7 @@ grammar =
# grammar.
Line: [
o 'Expression'
o 'ExpressionLine'
o 'Statement'
o 'FuncDirective'
]
@@ -125,6 +126,15 @@ grammar =
o 'Yield'
]
# Expressions which are written in single line and would otherwise require being
# wrapped in braces: E.g `a = b if do -> f a is 1`, `if f (a) -> a*2 then ...`,
# `for x in do (obj) -> f obj when x > 8 then f x`
ExpressionLine: [
o 'CodeLine'
o 'IfLine'
o 'OperationLine'
]
Yield: [
o 'YIELD', -> new Op $1, new Value new Literal ''
o 'YIELD Expression', -> new Op $1, $2
@@ -210,6 +220,7 @@ grammar =
ObjAssignable: [
o 'SimpleObjAssignable'
o '[ Expression ]', -> new Value new ComputedPropertyName $2
o 'AlphaNumeric'
]
@@ -266,6 +277,13 @@ grammar =
o 'FuncGlyph Block', -> new Code [], $2, $1
]
# The Codeline is the **Code** node with **Line** instead of indented **Block**.
CodeLine: [
o 'PARAM_START ParamList PARAM_END FuncGlyph Line', -> new Code $2, LOC(5)(Block.wrap [$5]), $4,
LOC(1)(new Literal $1)
o 'FuncGlyph Line', -> new Code [], LOC(2)(Block.wrap [$2]), $1
]
# CoffeeScript has two different symbols for functions. `->` is for ordinary
# functions, and `=>` is for functions bound to the current value of *this*.
FuncGlyph: [
@@ -439,6 +457,7 @@ grammar =
o 'EXPORT Identifier = INDENT Expression OUTDENT', -> new ExportNamedDeclaration new Assign $2, $5, null,
moduleDeclaration: 'export'
o 'EXPORT DEFAULT Expression', -> new ExportDefaultDeclaration $3
o 'EXPORT DEFAULT INDENT Object OUTDENT', -> new ExportDefaultDeclaration new Value $4
o 'EXPORT EXPORT_ALL FROM String', -> new ExportAllDeclaration new Literal($2), $4
o 'EXPORT { ExportSpecifierList OptComma } FROM String', -> new ExportNamedDeclaration new ExportSpecifierList($3), $7
]
@@ -492,7 +511,8 @@ grammar =
# The array literal.
Array: [
o '[ ]', -> new Arr []
o '[ ArgList OptComma ]', -> new Arr $2
o '[ Elisions ]', -> new Arr $2
o '[ ArgElisionList OptElisions ]', -> new Arr [].concat $2, $3
]
# Inclusive and exclusive range dots.
@@ -503,19 +523,21 @@ grammar =
# The CoffeeScript range literal.
Range: [
o '[ Expression RangeDots Expression ]', -> new Range $2, $4, $3
o '[ Expression RangeDots Expression ]', -> new Range $2, $4, $3
o '[ ExpressionLine RangeDots Expression ]', -> new Range $2, $4, $3
]
# Array slice literals.
Slice: [
o 'Expression RangeDots Expression', -> new Range $1, $3, $2
o 'Expression RangeDots', -> new Range $1, null, $2
o 'ExpressionLine RangeDots Expression', -> new Range $1, $3, $2
o 'ExpressionLine RangeDots', -> new Range $1, null, $2
o 'RangeDots Expression', -> new Range null, $2, $1
o 'RangeDots', -> new Range null, null, $1
]
# The **ArgList** is both the list of objects passed into a function call,
# as well as the contents of an array literal
# The **ArgList** is the list of objects passed into a function call
# (i.e. comma-separated expressions). Newlines work as well.
ArgList: [
o 'Arg', -> [$1]
@@ -528,16 +550,48 @@ grammar =
# Valid arguments are Blocks or Splats.
Arg: [
o 'Expression'
o 'ExpressionLine'
o 'Splat'
o '...', -> new Expansion
]
# The **ArgElisionList** is the list of objects, contents of an array literal
# (i.e. comma-separated expressions and elisions). Newlines work as well.
ArgElisionList: [
o 'ArgElision'
o 'ArgElisionList , ArgElision', -> $1.concat $3
o 'ArgElisionList OptElisions TERMINATOR ArgElision', -> $1.concat $2, $4
o 'INDENT ArgElisionList OptElisions OUTDENT', -> $2.concat $3
o 'ArgElisionList OptElisions INDENT ArgElisionList OptElisions OUTDENT', -> $1.concat $2, $4, $5
]
ArgElision: [
o 'Arg', -> [$1]
o 'Elisions Arg', -> $1.concat $2
]
OptElisions: [
o 'OptComma', -> []
o ', Elisions', -> [].concat $2
]
Elisions: [
o 'Elision', -> [$1]
o 'Elisions Elision', -> $1.concat $2
]
Elision: [
o ',', -> new Elision
]
# Just simple, comma-separated, required arguments (no fancy syntax). We need
# this to be separate from the **ArgList** for use in **Switch** blocks, where
# having the newlines wouldn't make sense.
SimpleArgs: [
o 'Expression'
o 'ExpressionLine'
o 'SimpleArgs , Expression', -> [].concat $1, $3
o 'SimpleArgs , ExpressionLine', -> [].concat $1, $3
]
# The variants of *try/catch/finally* exception handling blocks.
@@ -571,17 +625,27 @@ grammar =
]
# The condition portion of a while loop.
WhileLineSource: [
o 'WHILE ExpressionLine', -> new While $2
o 'WHILE ExpressionLine WHEN ExpressionLine', -> new While $2, guard: $4
o 'UNTIL ExpressionLine', -> new While $2, invert: true
o 'UNTIL ExpressionLine WHEN ExpressionLine', -> new While $2, invert: true, guard: $4
]
WhileSource: [
o 'WHILE Expression', -> new While $2
o 'WHILE Expression WHEN Expression', -> new While $2, guard: $4
o 'WHILE ExpressionLine WHEN Expression', -> new While $2, guard: $4
o 'UNTIL Expression', -> new While $2, invert: true
o 'UNTIL Expression WHEN Expression', -> new While $2, invert: true, guard: $4
o 'UNTIL ExpressionLine WHEN Expression', -> new While $2, invert: true, guard: $4
]
# 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.
While: [
o 'WhileSource Block', -> $1.addBody $2
o 'WhileLineSource Block', -> $1.addBody $2
o 'Statement WhileSource', -> $2.addBody LOC(1) Block.wrap([$1])
o 'Expression WhileSource', -> $2.addBody LOC(1) Block.wrap([$1])
o 'Loop', -> $1
@@ -596,20 +660,31 @@ grammar =
# Comprehensions can either be normal, with a block of expressions to execute,
# or postfix, with a single expression.
For: [
o 'Statement ForBody', -> new For $1, $2
o 'Expression ForBody', -> new For $1, $2
o 'ForBody Block', -> new For $2, $1
o 'Statement ForBody', -> $2.addBody $1
o 'Expression ForBody', -> $2.addBody $1
o 'ForBody Block', -> $1.addBody $2
o 'ForLineBody Block', -> $1.addBody $2
]
ForBody: [
o 'FOR Range', -> source: (LOC(2) new Value($2))
o 'FOR Range BY Expression', -> source: (LOC(2) new Value($2)), step: $4
o 'ForStart ForSource', -> $2.own = $1.own; $2.ownTag = $1.ownTag; $2.name = $1[0]; $2.index = $1[1]; $2
o 'FOR Range', -> new For [], source: (LOC(2) new Value($2))
o 'FOR Range BY Expression', -> new For [], source: (LOC(2) new Value($2)), step: $4
o 'ForStart ForSource', -> $1.addSource $2
]
ForLineBody: [
o 'FOR Range BY ExpressionLine', -> new For [], source: (LOC(2) new Value($2)), step: $4
o 'ForStart ForLineSource', -> $1.addSource $2
]
ForStart: [
o 'FOR ForVariables', -> $2
o 'FOR OWN ForVariables', -> $3.own = yes; $3.ownTag = (LOC(2) new Literal($2)); $3
o 'FOR ForVariables', -> new For [], name: $2[0], index: $2[1]
o 'FOR AWAIT ForVariables', ->
[name, index] = $3
new For [], {name, index, await: yes, awaitTag: (LOC(2) new Literal($2))}
o 'FOR OWN ForVariables', ->
[name, index] = $3
new For [], {name, index, own: yes, ownTag: (LOC(2) new Literal($2))}
]
# An array of all accepted values for a variable inside the loop.
@@ -633,22 +708,56 @@ grammar =
# clause. If it's an array comprehension, you can also choose to step through
# in fixed-size increments.
ForSource: [
o 'FORIN Expression', -> source: $2
o 'FOROF Expression', -> source: $2, object: yes
o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY Expression', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORFROM Expression', -> source: $2, from: yes
o 'FORFROM Expression WHEN Expression', -> source: $2, guard: $4, from: yes
o 'FORIN Expression', -> source: $2
o 'FOROF Expression', -> source: $2, object: yes
o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
o 'FORIN ExpressionLine WHEN Expression', -> source: $2, guard: $4
o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FOROF ExpressionLine WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY Expression', -> source: $2, step: $4
o 'FORIN ExpressionLine BY Expression', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression WHEN ExpressionLine BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN ExpressionLine BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN Expression BY ExpressionLine WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY ExpressionLine WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORFROM Expression', -> source: $2, from: yes
o 'FORFROM Expression WHEN Expression', -> source: $2, guard: $4, from: yes
o 'FORFROM ExpressionLine WHEN Expression', -> source: $2, guard: $4, from: yes
]
ForLineSource: [
o 'FORIN ExpressionLine', -> source: $2
o 'FOROF ExpressionLine', -> source: $2, object: yes
o 'FORIN Expression WHEN ExpressionLine', -> source: $2, guard: $4
o 'FORIN ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4
o 'FOROF Expression WHEN ExpressionLine', -> source: $2, guard: $4, object: yes
o 'FOROF ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY ExpressionLine', -> source: $2, step: $4
o 'FORIN ExpressionLine BY ExpressionLine', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN Expression BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN Expression WHEN ExpressionLine BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN ExpressionLine BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY Expression WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN Expression BY ExpressionLine WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY ExpressionLine WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORFROM ExpressionLine', -> source: $2, from: yes
o 'FORFROM Expression WHEN ExpressionLine', -> source: $2, guard: $4, from: yes
o 'FORFROM ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4, from: yes
]
Switch: [
o 'SWITCH Expression INDENT Whens OUTDENT', -> new Switch $2, $4
o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6
o 'SWITCH INDENT Whens OUTDENT', -> new Switch null, $3
o 'SWITCH INDENT Whens ELSE Block OUTDENT', -> new Switch null, $3, $5
o 'SWITCH Expression INDENT Whens OUTDENT', -> new Switch $2, $4
o 'SWITCH ExpressionLine INDENT Whens OUTDENT', -> new Switch $2, $4
o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6
o 'SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6
o 'SWITCH INDENT Whens OUTDENT', -> new Switch null, $3
o 'SWITCH INDENT Whens ELSE Block OUTDENT', -> new Switch null, $3, $5
]
Whens: [
@@ -679,12 +788,28 @@ grammar =
o 'Expression POST_IF Expression', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
]
IfBlockLine: [
o 'IF ExpressionLine Block', -> new If $2, $3, type: $1
o 'IfBlockLine ELSE IF ExpressionLine Block', -> $1.addElse LOC(3,5) new If $4, $5, type: $3
]
IfLine: [
o 'IfBlockLine'
o 'IfBlockLine ELSE Block', -> $1.addElse $3
o 'Statement POST_IF ExpressionLine', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
o 'Expression POST_IF ExpressionLine', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
]
# Arithmetic and logical operators, working on one or more operands.
# Here they are grouped by order of precedence. The actual precedence rules
# are defined at the bottom of the page. It would be shorter if we could
# combine most of these rules into a single generic *Operand OpSymbol Operand*
# -type rule, but in order to make the precedence binding possible, separate
# rules are necessary.
OperationLine: [
o 'UNARY ExpressionLine', -> new Op $1, $2
]
Operation: [
o 'UNARY Expression', -> new Op $1 , $2
o 'UNARY_MATH Expression', -> new Op $1 , $2

View File

@@ -192,13 +192,17 @@ exports.Lexer = class Lexer
# what CoffeeScript would normally interpret as calls to functions named
# `get` or `set`, i.e. `get({foo: function () {}})`.
else if tag is 'PROPERTY' and prev
if prev.spaced and prev[0] in CALLABLE and /^[gs]et$/.test(prev[1]) and @tokens[@tokens.length - 2][0] isnt '.'
@error "'#{prev[1]}' cannot be used as a keyword, or as a function call without parentheses", prev[2]
else
if prev.spaced and prev[0] in CALLABLE and /^[gs]et$/.test(prev[1]) and
@tokens.length > 1 and @tokens[@tokens.length - 2][0] not in ['.', '?.', '@']
@error "'#{prev[1]}' cannot be used as a keyword, or as a function call
without parentheses", prev[2]
else if @tokens.length > 2
prevprev = @tokens[@tokens.length - 2]
if prev[0] in ['@', 'THIS'] and prevprev and prevprev.spaced and /^[gs]et$/.test(prevprev[1]) and
@tokens[@tokens.length - 3][0] isnt '.'
@error "'#{prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses", prevprev[2]
if prev[0] in ['@', 'THIS'] and prevprev and prevprev.spaced and
/^[gs]et$/.test(prevprev[1]) and
@tokens[@tokens.length - 3][0] not in ['.', '?.', '@']
@error "'#{prevprev[1]}' cannot be used as a keyword, or as a
function call without parentheses", prevprev[2]
if tag is 'IDENTIFIER' and id in RESERVED
@error "reserved word '#{id}'", length: id.length
@@ -449,9 +453,11 @@ exports.Lexer = class Lexer
return 0 unless match = MULTI_DENT.exec chunk
indent = match[0]
@seenFor = no
@seenImport = no unless @importSpecifierList
@seenExport = no unless @exportSpecifierList
prev = @prev()
backslash = prev?[0] is '\\'
@seenFor = no unless backslash and @seenFor
@seenImport = no unless (backslash and @seenImport) or @importSpecifierList
@seenExport = no unless (backslash and @seenExport) or @exportSpecifierList
size = indent.length - 1 - indent.lastIndexOf '\n'
noNewlines = @unfinished()
@@ -472,7 +478,7 @@ exports.Lexer = class Lexer
if size > @indent
if noNewlines
@indebt = size - @indent
@indebt = size - @indent unless backslash
@suppressNewlines()
return indent.length
unless @tokens.length
@@ -556,7 +562,7 @@ exports.Lexer = class Lexer
# Check the previous token to detect if attribute is spread.
prevChar = if @tokens.length > 0 then @tokens[@tokens.length - 1][0] else ''
if firstChar is '<'
match = CSX_IDENTIFIER.exec @chunk[1...]
match = CSX_IDENTIFIER.exec(@chunk[1...]) or CSX_FRAGMENT_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`).
@@ -596,8 +602,8 @@ exports.Lexer = class Lexer
@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
match = CSX_IDENTIFIER.exec(@chunk[end...]) or CSX_FRAGMENT_IDENTIFIER.exec(@chunk[end...])
if not match or match[1] isnt csxTag.name
@error "expected corresponding CSX closing tag for #{csxTag.name}",
csxTag.origin[2]
afterTag = end + csxTag.name.length
@@ -1178,6 +1184,11 @@ CSX_IDENTIFIER = /// ^
( (?: (?!\s)[\.\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s and `.`s.
///
# Fragment: <></>
CSX_FRAGMENT_IDENTIFIER = /// ^
()> # Ends immediately with `>`.
///
CSX_ATTRIBUTE = /// ^
(?!\d)
( (?: (?!\s)[\-$\w\x7f-\uffff] )+ ) # Like `IDENTIFIER`, but includes `-`s.
@@ -1250,9 +1261,22 @@ REGEX = /// ^
///
REGEX_FLAGS = /^\w*/
VALID_FLAGS = /^(?!.*(.).*\1)[imguy]*$/
VALID_FLAGS = /^(?!.*(.).*\1)[gimsuy]*$/
HEREGEX = /// ^(?: [^\\/#] | \\[\s\S] | /(?!//) | \#(?!\{) )* ///
HEREGEX = /// ^
(?:
# Match any character, except those that need special handling below.
[^\\/#\s]
# Match `\` followed by any character.
| \\[\s\S]
# Match any `/` except `///`.
| /(?!//)
# Match `#` which is not part of interpolation, e.g. `#{}`.
| \#(?!\{)
# Comments consume everything until the end of the line, including `///`.
| \s+(?:#(?!\{).*)?
)*
///
HEREGEX_OMIT = ///
((?:\\\\)+) # Consume (and preserve) an even number of backslashes.
@@ -1354,4 +1378,4 @@ INDENTABLE_CLOSERS = [')', '}', ']']
# Tokens that, when appearing at the end of a line, suppress a following TERMINATOR/INDENT token
UNFINISHED = ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-',
'**', 'SHIFT', 'RELATION', 'COMPARE', '&', '^', '|', '&&', '||',
'BIN?', 'EXTENDS', 'DEFAULT']
'BIN?', 'EXTENDS']

View File

@@ -765,6 +765,10 @@ exports.CSXTag = class CSXTag extends IdentifierLiteral
exports.PropertyName = class PropertyName extends Literal
isAssignable: YES
exports.ComputedPropertyName = class ComputedPropertyName extends PropertyName
compileNode: (o) ->
[@makeCode('['), @value.compileToFragments(o, LEVEL_LIST)..., @makeCode(']')]
exports.StatementLiteral = class StatementLiteral extends Literal
isStatement: YES
@@ -910,6 +914,10 @@ exports.Value = class Value extends Base
return no if @properties.length
(@base instanceof Obj) and (not onlyGenerated or @base.generated)
isElision: ->
return no unless @base instanceof Arr
@base.hasElision()
isSplice: ->
[..., lastProp] = @properties
lastProp instanceof Slice
@@ -948,11 +956,21 @@ exports.Value = class Value extends Base
compileNode: (o) ->
@base.front = @front
props = @properties
fragments = @base.compileToFragments o, (if props.length then LEVEL_ACCESS else null)
if props.length and @base.cached?
# Cached fragments enable correct order of the compilation,
# and reuse of variables in the scope.
# Example:
# `a(x = 5).b(-> x = 6)` should compile in the same order as
# `a(x = 5); b(-> x = 6)`
# (see issue #4437, https://github.com/jashkenas/coffeescript/issues/4437)
fragments = @base.cached
else
fragments = @base.compileToFragments o, (if props.length then LEVEL_ACCESS else null)
if props.length and SIMPLENUM.test fragmentsToText fragments
fragments.push @makeCode '.'
for prop in props
fragments.push (prop.compileToFragments o)...
fragments
# Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
@@ -1112,6 +1130,19 @@ exports.Call = class Call extends Base
return @compileCSX o if @csx
@variable?.front = @front
compiledArgs = []
# If variable is `Accessor` fragments are cached and used later
# in `Value::compileNode` to ensure correct order of the compilation,
# and reuse of variables in the scope.
# Example:
# `a(x = 5).b(-> x = 6)` should compile in the same order as
# `a(x = 5); b(-> x = 6)`
# (see issue #4437, https://github.com/jashkenas/coffeescript/issues/4437)
varAccess = @variable?.properties?[0] instanceof Access
argCode = (arg for arg in (@args || []) when arg instanceof Code)
if argCode.length > 0 and varAccess and not @variable.base.cached
[cache] = @variable.base.cache o, LEVEL_ACCESS, -> no
@variable.base.cached = cache
for arg, argIndex in @args
if argIndex then compiledArgs.push @makeCode ", "
compiledArgs.push (arg.compileToFragments o, LEVEL_LIST)...
@@ -1317,20 +1348,35 @@ exports.Range = class Range extends Base
idx = del o, 'index'
idxName = del o, 'name'
namedIndex = idxName and idxName isnt idx
varPart = "#{idx} = #{@fromC}"
varPart =
if known and not namedIndex
"var #{idx} = #{@fromC}"
else
"#{idx} = #{@fromC}"
varPart += ", #{@toC}" if @toC isnt @toVar
varPart += ", #{@step}" if @step isnt @stepVar
[lt, gt] = ["#{idx} <#{@equals}", "#{idx} >#{@equals}"]
# Generate the condition.
condPart = if @stepNum?
if @stepNum > 0 then "#{lt} #{@toVar}" else "#{gt} #{@toVar}"
else if known
[from, to] = [@fromNum, @toNum]
if from <= to then "#{lt} #{to}" else "#{gt} #{to}"
else
cond = if @stepVar then "#{@stepVar} > 0" else "#{@fromVar} <= #{@toVar}"
"#{cond} ? #{lt} #{@toVar} : #{gt} #{@toVar}"
[from, to] = [@fromNum, @toNum]
# Always check if the `step` isn't zero to avoid the infinite loop.
stepNotZero = "#{ @stepNum ? @stepVar } !== 0"
stepCond = "#{ @stepNum ? @stepVar } > 0"
lowerBound = "#{lt} #{ if known then to else @toVar }"
upperBound = "#{gt} #{ if known then to else @toVar }"
condPart =
if @step?
if @stepNum? and @stepNum isnt 0
if @stepNum > 0 then "#{lowerBound}" else "#{upperBound}"
else
"#{stepNotZero} && (#{stepCond} ? #{lowerBound} : #{upperBound})"
else
if known
"#{ if from <= to then lt else gt } #{to}"
else
"(#{@fromVar} <= #{@toVar} ? #{lowerBound} : #{upperBound})"
cond = if @stepVar then "#{@stepVar} > 0" else "#{@fromVar} <= #{@toVar}"
# Generate the step.
stepPart = if @stepVar
@@ -1361,9 +1407,9 @@ exports.Range = class Range extends Base
range.pop() if @exclusive
return [@makeCode "[#{ range.join(', ') }]"]
idt = @tab + TAB
i = o.scope.freeVariable 'i', single: true
result = o.scope.freeVariable 'results'
pre = "\n#{idt}#{result} = [];"
i = o.scope.freeVariable 'i', single: true, reserve: no
result = o.scope.freeVariable 'results', reserve: no
pre = "\n#{idt}var #{result} = [];"
if known
o.index = i
body = fragmentsToText @compileNode o
@@ -1393,8 +1439,12 @@ exports.Slice = class Slice extends Base
# `9e9` should be safe because `9e9` > `2**32`, the max array length.
compileNode: (o) ->
{to, from} = @range
fromCompiled = from and from.compileToFragments(o, LEVEL_PAREN) or [@makeCode '0']
# TODO: jwalton - move this into the 'if'?
# Handle an expression in the property access, e.g. `a[!b in c..]`.
if from?.shouldCache()
from = new Value new Parens from
if to?.shouldCache()
to = new Value new Parens to
fromCompiled = from?.compileToFragments(o, LEVEL_PAREN) or [@makeCode '0']
if to
compiled = to.compileToFragments o, LEVEL_PAREN
compiledText = fragmentsToText compiled
@@ -1425,7 +1475,9 @@ exports.Obj = class Obj extends Base
message = isUnassignable prop.unwrapAll().value
prop.error message if message
prop = prop.value if prop instanceof Assign and prop.context is 'object'
prop = prop.value if prop instanceof Assign and
prop.context is 'object' and
prop.value?.base not instanceof Arr
return no unless prop.isAssignable()
yes
@@ -1437,15 +1489,23 @@ exports.Obj = class Obj extends Base
return yes for prop in @properties when prop instanceof Splat
no
# Move rest property to the end of the list.
# `{a, rest..., b} = obj` -> `{a, b, rest...} = obj`
# `foo = ({a, rest..., b}) ->` -> `foo = {a, b, rest...}) ->`
reorderProperties: ->
props = @properties
splatProps = (i for prop, i in props when prop instanceof Splat)
props[splatProps[1]].error "multiple spread elements are disallowed" if splatProps?.length > 1
splatProp = props.splice splatProps[0], 1
@objects = @properties = [].concat props, splatProp
compileNode: (o) ->
@reorderProperties() if @hasSplat() and @lhs
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() and not @csx
idt = o.indent += TAB
lastNode = @lastNode @properties
@@ -1497,7 +1557,16 @@ exports.Obj = class Obj extends Base
[key, value] = prop.base.cache o
key = new PropertyName key.value if key instanceof IdentifierLiteral
prop = new Assign key, value, 'object'
else if not prop.bareLiteral?(IdentifierLiteral)
else if key instanceof Value and key.base instanceof ComputedPropertyName
# `{ [foo()] }` output as `{ [ref = foo()]: ref }`.
if prop.base.value.shouldCache()
[key, value] = prop.base.value.cache o
key = new ComputedPropertyName key.value if key instanceof IdentifierLiteral
prop = new Assign key, value, 'object'
else
# `{ [expression] }` output as `{ [expression]: expression }`.
prop = new Assign key, prop.base.value, 'object'
else if not prop.bareLiteral?(IdentifierLiteral) and prop not instanceof Splat
prop = new Assign prop, prop, 'object'
if indent then answer.push @makeCode indent
answer.push prop.compileToFragments(o, LEVEL_TOP)...
@@ -1516,30 +1585,6 @@ 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 = _extends({}, {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
_extends = new Value new Literal utility '_extends', o
(new Call _extends, slices).compileToFragments o
compileCSXAttributes: (o) ->
props = @properties
answer = []
@@ -1561,6 +1606,10 @@ exports.Arr = class Arr extends Base
children: ['objects']
hasElision: ->
return yes for obj in @objects when obj instanceof Elision
no
isAssignable: ->
return no unless @objects.length
@@ -1575,6 +1624,9 @@ exports.Arr = class Arr extends Base
compileNode: (o) ->
return [@makeCode '[]'] unless @objects.length
o.indent += TAB
fragmentIsElision = (fragment) -> fragmentsToText(fragment).trim() is ','
# Detect if `Elisions` at the beginning of the array are processed (e.g. [, , , a]).
passedElision = no
answer = []
for obj, objIndex in @objects
@@ -1590,6 +1642,7 @@ exports.Arr = class Arr extends Base
unwrappedObj.lhs = yes if unwrappedObj instanceof Arr or unwrappedObj instanceof Obj
compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects)
olen = compiledObjs.length
# If `compiledObjs` includes newlines, we will output this as a multiline
# array (i.e. with a newline and indentation after the `[`). If an element
# contains line comments, that should also trigger multiline output since
@@ -1604,14 +1657,17 @@ exports.Arr = class Arr extends Base
fragment.code = fragment.code.trim()
else if index isnt 0 and includesLineCommentsOnNonFirstElement is no and hasLineComments fragment
includesLineCommentsOnNonFirstElement = yes
if index isnt 0
# Add ', ' if all `Elisions` from the beginning of the array are processed (e.g. [, , , a]) and
# element isn't `Elision` or last element is `Elision` (e.g. [a,,b,,])
if index isnt 0 and passedElision and (not fragmentIsElision(fragments) or index is olen - 1)
answer.push @makeCode ', '
passedElision = passedElision or not fragmentIsElision fragments
answer.push fragments...
if includesLineCommentsOnNonFirstElement or '\n' in fragmentsToText(answer)
for fragment, fragmentIndex in answer
if fragment.isHereComment
fragment.code = "#{multident(fragment.code, o.indent, no)}\n#{o.indent}"
else if fragment.code is ', '
else if fragment.code is ', ' and not fragment?.isElision
fragment.code = ",\n#{o.indent}"
answer.unshift @makeCode "[\n#{o.indent}"
answer.push @makeCode "\n#{@tab}]"
@@ -1843,20 +1899,20 @@ exports.ExecutableClassBody = class ExecutableClassBody extends Base
if argumentsNode = @body.contains isLiteralArguments
argumentsNode.error "Class bodies shouldn't reference arguments"
@name = @class.name ? @defaultClassVariableName
directives = @walkBody()
@setContext()
ident = new IdentifierLiteral @name
params = []
args = []
args = [new ThisLiteral]
wrapper = new Code params, @body
klass = new Parens new Call wrapper, args
klass = new Parens new Call (new Value wrapper, [new Access new PropertyName 'call']), args
@body.spaced = true
o.classScope = wrapper.makeScope o.scope
@name = @class.name ? o.classScope.freeVariable @defaultClassVariableName
ident = new IdentifierLiteral @name
directives = @walkBody()
@setContext()
if @class.hasNameClash
parent = new IdentifierLiteral o.classScope.freeVariable 'superClass'
wrapper.params.push new Param parent
@@ -2141,17 +2197,17 @@ exports.Assign = class Assign extends Base
# know that, so that those nodes know that theyre assignable as
# destructured variables.
@variable.base.lhs = yes
return @compileDestructuring o unless @variable.isAssignable()
# Object destructuring. Can be removed once ES proposal hits Stage 4.
objDestructAnswer = @compileObjectDestruct(o) if @variable.isObject() and @variable.contains (node) ->
node instanceof Obj and node.hasSplat()
return objDestructAnswer if objDestructAnswer
unless @variable.isAssignable()
if @variable.isObject() and @variable.base.hasSplat()
return @compileObjectDestruct o
else
return @compileDestructuring o
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @context in ['||=', '&&=', '?=']
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
return @compileSpecialMath o if @context in ['//=', '%%=']
unless @context
if not @context or @context is '**='
varBase = @variable.unwrapAll()
unless varBase.isAssignable()
@variable.error "'#{@variable.compile o}' can't be assigned"
@@ -2209,97 +2265,25 @@ exports.Assign = class Assign extends Base
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 o.level is LEVEL_TOP and isValue and @variable.base instanceof Obj and not @nestedLhs and not (@param is yes)
# The assignment is wrapped in parentheses if 'o.level' has lower precedence than LEVEL_LIST (3)
# (i.e. LEVEL_COND (4), LEVEL_OP (5) or LEVEL_ACCESS (6)), or if we're destructuring object, e.g. {a,b} = obj.
if o.level > LEVEL_LIST or isValue and @variable.base instanceof Obj and not @nestedLhs and not (@param is yes)
@wrapInParentheses answer
else
answer
# Check object destructuring variable for rest elements;
# can be removed once ES proposal hits Stage 4.
# Object rest property is not assignable: `{{a}...}`
compileObjectDestruct: (o) ->
# 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 isnt key
if cached or not key.isAssignable()
key
else
new Literal "'#{key.compileWithoutComments o}'"
# Recursive function for searching and storing rest elements in objects.
# e.g. `{[properties...]} = source`.
traverseRest = (properties, source) =>
restElements = []
restIndex = undefined
source = new Value source unless source.properties?
for prop, index in properties
nestedSourceDefault = nestedSource = nestedProperties = null
if prop instanceof Assign
# prop is `k: expr`, we need to check `expr` for nested splats
if prop.value.isObject?()
# prop is `k = {...} `
continue unless prop.context is 'object'
# 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.push 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.
valueRefTemp =
if @value.shouldCache()
new IdentifierLiteral o.scope.freeVariable 'ref', reserve: false
else
@value.base
# Find all rest elements.
restElements = traverseRest @variable.base.properties, valueRefTemp
return no unless restElements and restElements.length > 0
[@value, valueRef] = @value.cache o
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 new Value(restElement.name), value, null, param: if @param then 'alwaysDeclare' else null
fragments = result.compileToFragments o
if o.level is LEVEL_TOP
# Remove leading tab and trailing semicolon
fragments.shift()
fragments.pop()
fragments
@variable.base.reorderProperties()
{properties: props} = @variable.base
[..., splat] = props
splatProp = splat.name
assigns = []
refVal = new Value new IdentifierLiteral o.scope.freeVariable 'ref'
props.splice -1, 1, new Splat refVal
assigns.push new Assign(new Value(new Obj props), @value).compileToFragments o, LEVEL_LIST
assigns.push new Assign(new Value(splatProp), refVal).compileToFragments o, LEVEL_LIST
@joinFragmentArrays assigns, ', '
# Brief implementation of recursive pattern matching, when assigning array or
# object literals to a value. Peeks at their properties to assign inner names.
@@ -2320,47 +2304,33 @@ exports.Assign = class Assign extends Base
if olen is 1 and obj instanceof Expansion
obj.error 'Destructuring assignment has no target'
isObject = @variable.isObject()
# Count all `Splats`: [a, b, c..., d, e]
splats = (i for obj, i in objects when obj instanceof Splat)
# Count all `Expansions`: [a, b, ..., c, d]
expans = (i for obj, i in objects when obj instanceof Expansion)
# Combine splats and expansions.
splatsAndExpans = [splats..., expans...]
# Show error if there is more than one `Splat`, or `Expansion`.
# Examples: [a, b, c..., d, e, f...], [a, b, ..., c, d, ...], [a, b, ..., c, d, e...]
if splatsAndExpans.length > 1
# Sort 'splatsAndExpans' so we can show error at first disallowed token.
objects[splatsAndExpans.sort()[1]].error "multiple splats/expansions are disallowed in an assignment"
# Special case for when there's only one thing destructured off of
# something. `{a} = b`, `[a] = b`, `{a: b} = c`
if top and olen is 1 and obj not instanceof Splat
# Pick the property straight off the value when theres just one to pick
# (no need to cache the value into a variable).
defaultValue = undefined
if obj instanceof Assign and obj.context is 'object'
# A regular object pattern-match.
{variable: {base: idx}, value: obj} = obj
if obj instanceof Assign
defaultValue = obj.value
obj = obj.variable
else
if obj instanceof Assign
defaultValue = obj.value
obj = obj.variable
idx = if isObject
# A shorthand `{a, b, @c} = val` pattern-match.
if obj.this
obj.properties[0].name
else
new PropertyName obj.unwrap().value
else
# A regular array pattern-match.
new NumberLiteral 0
acc = idx.unwrap() instanceof PropertyName
value = new Value value
value.properties.push new (if acc then Access else Index) idx
message = isUnassignable obj.unwrap().value
obj.error message if message
if defaultValue
defaultValue.isDefaultValue = yes
value = new Op '?', value, defaultValue
return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
isSplat = splats?.length > 0
isExpans = expans?.length > 0
vvar = value.compileToFragments o, LEVEL_LIST
vvarText = fragmentsToText vvar
assigns = []
expandedIdx = false
pushAssign = (variable, val) =>
assigns.push new Assign(variable, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
if isSplat
splatVar = objects[splats[0]].name.unwrap()
if splatVar instanceof Arr or splatVar instanceof Obj
splatVarRef = new IdentifierLiteral o.scope.freeVariable 'ref'
objects[splats[0]].name = splatVarRef
splatVarAssign = -> pushAssign new Value(splatVar), splatVarRef
# 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
@@ -2371,75 +2341,104 @@ exports.Assign = class Assign extends Base
vvar = [@makeCode ref]
vvarText = ref
# And here comes the big loop that handles all of these cases:
# `[a, b] = c`
# `[a..., b] = c`
# `[..., a, b] = c`
# `[@a, b] = c`
# `[a = 1, b] = c`
# `{a, b} = c`
# `{@a, b} = c`
# `{a = 1, b} = c`
# etc.
for obj, i in objects
idx = i
if not expandedIdx and obj instanceof Splat
name = obj.name.unwrap().value
obj = obj.unwrap()
val = "#{olen} <= #{vvarText}.length ? #{utility 'slice', o}.call(#{vvarText}, #{i}"
rest = olen - i - 1
if rest isnt 0
ivar = o.scope.freeVariable 'i', single: true
val += ", #{ivar} = #{vvarText}.length - #{rest}) : (#{ivar} = #{i}, [])"
else
val += ") : []"
val = new Literal val
expandedIdx = "#{ivar}++"
else if not expandedIdx and obj instanceof Expansion
rest = olen - i - 1
if rest isnt 0
if rest is 1
expandedIdx = "#{vvarText}.length - 1"
else
ivar = o.scope.freeVariable 'i', single: true
val = new Literal "#{ivar} = #{vvarText}.length - #{rest}"
expandedIdx = "#{ivar}++"
assigns.push val.compileToFragments o, LEVEL_LIST
continue
else
if obj instanceof Splat or obj instanceof Expansion
obj.error "multiple splats/expansions are disallowed in an assignment"
defaultValue = undefined
if obj instanceof Assign and obj.context is 'object'
# A regular object pattern-match.
{variable: {base: idx}, value: obj} = obj
if obj instanceof Assign
defaultValue = obj.value
obj = obj.variable
else
if obj instanceof Assign
defaultValue = obj.value
obj = obj.variable
idx = if isObject
# A shorthand `{a, b, @c} = val` pattern-match.
if obj.this
obj.properties[0].name
else
new PropertyName obj.unwrap().value
else
# A regular array pattern-match.
new Literal expandedIdx or idx
name = obj.unwrap().value
acc = idx.unwrap() instanceof PropertyName
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
if defaultValue
defaultValue.isDefaultValue = yes
val = new Op '?', val, defaultValue
if name?
message = isUnassignable name
obj.error message if message
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
slicer = (type) -> (vvar, start, end = no) ->
vvar = new IdentifierLiteral vvar unless vvar instanceof Value
args = [vvar, new NumberLiteral(start)]
args.push new NumberLiteral end if end
slice = new Value (new IdentifierLiteral utility type, o), [new Access new PropertyName 'call']
new Value new Call slice, args
# Helper which outputs `[].slice` code.
compSlice = slicer "slice"
# Helper which outputs `[].splice` code.
compSplice = slicer "splice"
# Check if `objects` array contains any instance of `Assign`, e.g. {a:1}.
hasObjAssigns = (objs) ->
(i for obj, i in objs when obj instanceof Assign and obj.context is 'object')
# Check if `objects` array contains any unassignable object.
objIsUnassignable = (objs) ->
return yes for obj in objs when not obj.isAssignable()
no
# `objects` are complex when there is object assign ({a:1}),
# unassignable object, or just a single node.
complexObjects = (objs) ->
hasObjAssigns(objs).length or objIsUnassignable(objs) or olen is 1
# "Complex" `objects` are processed in a loop.
# Examples: [a, b, {c, r...}, d], [a, ..., {b, r...}, c, d]
loopObjects = (objs, vvar, vvarTxt) =>
for obj, i in objs
# `Elision` can be skipped.
continue if obj instanceof Elision
# If `obj` is {a: 1}
if obj instanceof Assign and obj.context is 'object'
{variable: {base: idx}, value: vvar} = obj
{variable: vvar} = vvar if vvar instanceof Assign
idx =
if vvar.this
vvar.properties[0].name
else
new PropertyName vvar.unwrap().value
acc = idx.unwrap() instanceof PropertyName
vval = new Value value, [new (if acc then Access else Index) idx]
else
# `obj` is [a...], {a...} or a
vvar = switch
when obj instanceof Splat then new Value obj.name
else obj
vval = switch
when obj instanceof Splat then compSlice(vvarTxt, i)
else new Value new Literal(vvarTxt), [new Index new NumberLiteral i]
message = isUnassignable vvar.unwrap().value
vvar.error message if message
pushAssign vvar, vval
# "Simple" `objects` can be split and compiled to arrays, [a, b, c] = arr, [a, b, c...] = arr
assignObjects = (objs, vvar, vvarTxt) =>
vvar = new Value new Arr(objs, yes)
vval = if vvarTxt instanceof Value then vvarTxt else new Value new Literal(vvarTxt)
pushAssign vvar, vval
processObjects = (objs, vvar, vvarTxt) ->
if complexObjects objs
loopObjects objs, vvar, vvarTxt
else
assignObjects objs, vvar, vvarTxt
# In case there is `Splat` or `Expansion` in `objects`,
# we can split array in two simple subarrays.
# `Splat` [a, b, c..., d, e] can be split into [a, b, c...] and [d, e].
# `Expansion` [a, b, ..., c, d] can be split into [a, b] and [c, d].
# Examples:
# a) `Splat`
# CS: [a, b, c..., d, e] = arr
# JS: [a, b, ...c] = arr, [d, e] = splice.call(c, -2)
# b) `Expansion`
# CS: [a, b, ..., d, e] = arr
# JS: [a, b] = arr, [d, e] = slice.call(arr, -2)
if splatsAndExpans.length
expIdx = splatsAndExpans[0]
leftObjs = objects.slice 0, expIdx + (if isSplat then 1 else 0)
rightObjs = objects.slice expIdx + 1
processObjects leftObjs, vvar, vvarText if leftObjs.length isnt 0
if rightObjs.length isnt 0
# Slice or splice `objects`.
refExp = switch
when isSplat then compSplice new Value(objects[expIdx].name), rightObjs.length * -1
when isExpans then compSlice vvarText, rightObjs.length * -1
if complexObjects rightObjs
restVar = refExp
refExp = o.scope.freeVariable 'ref'
assigns.push [@makeCode(refExp + ' = '), restVar.compileToFragments(o, LEVEL_LIST)...]
processObjects rightObjs, vvar, refExp
else
# There is no `Splat` or `Expansion` in `objects`.
processObjects objects, vvar, vvarText
splatVarAssign?()
assigns.push vvar unless top or @subpattern
fragments = @joinFragmentArrays assigns, ', '
if o.level < LEVEL_LIST then fragments else @wrapInParentheses fragments
@@ -2460,7 +2459,7 @@ exports.Assign = class Assign extends Base
fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
if o.level <= LEVEL_LIST then fragments else @wrapInParentheses fragments
# Convert special math assignment operators like `a **= b` to the equivalent
# Convert special math assignment operators like `a //= b` to the equivalent
# extended form `a = a ** b` and then compiles that.
compileSpecialMath: (o) ->
[left, right] = @variable.cacheReference o
@@ -2522,8 +2521,8 @@ exports.Code = class Code extends Base
@isGenerator = yes
if (node instanceof Op and node.isAwait()) or node instanceof AwaitReturn
@isAsync = yes
if @isGenerator and @isAsync
node.error "function can't contain both yield and await"
if node instanceof For and node.isAwait()
@isAsync = yes
children: ['params', 'body']
@@ -2562,14 +2561,24 @@ exports.Code = class Code extends Base
# Check for duplicate parameters and separate `this` assignments.
paramNames = []
@eachParamName (name, node, param) ->
@eachParamName (name, node, param, obj) ->
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
target = new IdentifierLiteral o.scope.freeVariable name
param.renameParam node, target
target = new IdentifierLiteral o.scope.freeVariable name, reserve: no
# `Param` is object destructuring with a default value: ({@prop = 1}) ->
# In a case when the variable name is already reserved, we have to assign
# a new variable name to the destructured variable: ({prop:prop1 = 1}) ->
replacement =
if param.name instanceof Obj and obj instanceof Assign and
obj.operatorToken.value is '='
new Assign (new IdentifierLiteral name), target, 'object' #, operatorToken: new Literal ':'
else
target
param.renameParam node, replacement
thisAssignments.push new Assign node, target
# Parse the parameters, adding them to the list of parameters to put in the
@@ -2591,7 +2600,7 @@ exports.Code = class Code extends Base
param.error 'an expansion parameter cannot be the only parameter in a function definition'
haveSplatParam = yes
if param.splat
if param.name instanceof Arr
if param.name instanceof Arr or param.name instanceof Obj
# Splat arrays are treated oddly by ES; deal with them the legacy
# way in the function body. TODO: Should this be handled in the
# function parameter list, and if so, how?
@@ -2645,17 +2654,7 @@ exports.Code = class Code extends Base
if param.name instanceof Arr or param.name instanceof Obj
# This parameter is destructured.
param.name.lhs = yes
# 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, null, param: 'alwaysDeclare'
# 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 unless param.shouldCache()
unless param.shouldCache()
param.name.eachName (prop) ->
o.scope.parameter prop.value
else
@@ -2697,6 +2696,12 @@ exports.Code = class Code extends Base
@body.expressions.unshift new Call(boundMethodCheck, [new Value(new ThisLiteral), @classVariable])
@body.makeReturn() unless wasEmpty or @noReturn
# JavaScript doesnt allow bound (`=>`) functions to also be generators.
# This is usually caught via `Op::compileContinuation`, but double-check:
if @bound and @isGenerator
yieldNode = @body.contains (node) -> node instanceof Op and node.operator is 'yield'
(yieldNode or @).error 'yield cannot occur inside bound (fat arrow) functions'
# Assemble the output
modifiers = []
modifiers.push 'static' if @isMethod and @isStatic
@@ -2794,7 +2799,9 @@ exports.Code = class Code extends Base
# cannot be given an argument with a reference to `this`, as that would
# be referencing `this` before calling `super`.
unless child.variable.accessor
Block.wrap(child.args).traverseChildren yes, (node) =>
childArgs = child.args.filter (arg) ->
arg not instanceof Class and (arg not instanceof Code or arg.bound)
Block.wrap(childArgs).traverseChildren yes, (node) =>
node.error "Can't call super with @params in derived class constructors" if node.this
seenSuper = yes
iterator child
@@ -2852,12 +2859,14 @@ exports.Param = class Param extends Base
# `name` is the name of the parameter and `node` is the AST node corresponding
# to that name.
eachName: (iterator, name = @name) ->
atParam = (obj) => iterator "@#{obj.properties[0].name.value}", obj, @
atParam = (obj, originalObj = null) => iterator "@#{obj.properties[0].name.value}", obj, @, originalObj
# * simple literals `foo`
return iterator name.value, name, @ if name instanceof Literal
# * at-params `@foo`
return atParam name if name instanceof Value
for obj in name.objects ? []
# Save original obj.
nObj = obj
# * destructured parameter with default value
if obj instanceof Assign and not obj.context?
obj = obj.variable
@@ -2879,9 +2888,11 @@ exports.Param = class Param extends Base
@eachName iterator, obj.base
# * at-params within destructured parameters `{@foo}`
else if obj.this
atParam obj
atParam obj, nObj
# * simple destructured parameters {foo}
else iterator obj.base.value, obj.base, @
else if obj instanceof Elision
obj
else if obj not instanceof Expansion
obj.error "illegal parameter #{obj.compile()}"
return
@@ -2894,7 +2905,14 @@ exports.Param = class Param extends Base
if parent instanceof Obj
key = node
key = node.properties[0].name if node.this
new Assign new Value(key), newNode, 'object'
# No need to assign a new variable for the destructured variable if the variable isn't reserved.
# Examples:
# `({@foo}) ->` should compile to `({foo}) { this.foo = foo}`
# `foo = 1; ({@foo}) ->` should compile to `foo = 1; ({foo:foo1}) { this.foo = foo1 }`
if node.this and key.value is newNode.value
new Value newNode
else
new Assign new Value(key), newNode, 'object'
else
newNode
@@ -2911,7 +2929,10 @@ exports.Splat = class Splat extends Base
children: ['name']
shouldCache: -> no
isAssignable: ->
return no if @name instanceof Obj or @name instanceof Parens
@name.isAssignable() and (not @name.isAtomic or @name.isAtomic())
assigns: (name) ->
@@ -2938,6 +2959,28 @@ exports.Expansion = class Expansion extends Base
eachName: (iterator) ->
#### Elision
# Array elision element (for example, [,a, , , b, , c, ,]).
exports.Elision = class Elision extends Base
isAssignable: YES
shouldCache: NO
compileToFragments: (o, level) ->
fragment = super o, level
fragment.isElision = yes
fragment
compileNode: (o) ->
[@makeCode ', ']
asReference: (o) ->
this
eachName: (iterator) ->
#### While
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
@@ -3114,7 +3157,6 @@ exports.Op = class Op extends Base
return @compileChain o if isChain
switch @operator
when '?' then @compileExistence o, @second.isDefaultValue
when '**' then @compilePower o
when '//' then @compileFloorDivision o
when '%%' then @compileModulo o
else
@@ -3181,11 +3223,6 @@ exports.Op = class Op extends Base
parts.push [@makeCode ")"] if o.level >= LEVEL_PAREN
@joinFragmentArrays parts, ''
compilePower: (o) ->
# Make a Math.pow call
pow = new Value new IdentifierLiteral('Math'), [new Access new PropertyName 'pow']
new Call(pow, [@first, @second]).compileToFragments o
compileFloorDivision: (o) ->
floor = new Value new IdentifierLiteral('Math'), [new Access new PropertyName 'floor']
second = if @second.shouldCache() then new Parens @second else @second
@@ -3482,15 +3519,27 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
exports.For = class For extends While
constructor: (body, source) ->
super()
{@source, @guard, @step, @name, @index} = source
@body = Block.wrap [body]
@own = source.own?
@object = source.object?
@from = source.from?
@addBody body
@addSource source
children: ['body', 'source', 'guard', 'step']
isAwait: -> @await ? no
addBody: (body) ->
@body = Block.wrap [body]
this
addSource: (source) ->
{@source = no} = source
attribs = ["name", "index", "guard", "step", "own", "ownTag", "await", "awaitTag", "object", "from"]
@[attr] = source[attr] ? @[attr] for attr in attribs
return this unless @source
@index.error 'cannot use index with for-from' if @from and @index
source.ownTag.error "cannot use own with for-#{if @from then 'from' else 'in'}" if @own and not @object
@ownTag.error "cannot use own with for-#{if @from then 'from' else 'in'}" if @own and not @object
[@name, @index] = [@index, @name] if @object
@index.error 'index cannot be a pattern matching expression' if @index?.isArray?() or @index?.isObject?()
@awaitTag.error 'await must be used with for-from' if @await and not @from
@range = @source instanceof Value and @source.base instanceof Range and not @source.properties.length and not @from
@pattern = @name instanceof Value
@index.error 'indexes do not apply to range loops' if @range and @index
@@ -3509,8 +3558,7 @@ exports.For = class For extends While
comment.newLine = comment.unshift = yes for comment in node.comments
moveComments node, @[attribute]
moveComments @[attribute], @
children: ['body', 'source', 'guard', 'step']
this
# Welcome to the hairiest method in all of CoffeeScript. Handles the inner
# loop, filtering, stepping, and result saving for array, object, and range
@@ -3582,47 +3630,31 @@ exports.For = class For extends While
body = Block.wrap [new If @guard, body] if @guard
if @pattern
body.expressions.unshift new Assign @name, if @from then new IdentifierLiteral kvar else new Literal "#{svar}[#{kvar}]"
defPartFragments = [].concat @makeCode(defPart), @pluckDirectCall(o, body)
varPart = "\n#{idt1}#{namePart};" if namePart
if @object
forPartFragments = [@makeCode("#{kvar} in #{svar}")]
guardPart = "\n#{idt1}if (!#{utility 'hasProp', o}.call(#{svar}, #{kvar})) continue;" if @own
else if @from
forPartFragments = [@makeCode("#{kvar} of #{svar}")]
if @await
forPartFragments = new Op 'await', new Parens new Literal "#{kvar} of #{svar}"
forPartFragments = forPartFragments.compileToFragments o, LEVEL_TOP
else
forPartFragments = [@makeCode("#{kvar} of #{svar}")]
bodyFragments = body.compileToFragments merge(o, indent: idt1), LEVEL_TOP
if bodyFragments and bodyFragments.length > 0
bodyFragments = [].concat @makeCode('\n'), bodyFragments, @makeCode('\n')
fragments = []
if defPartFragments? and fragmentsToText(defPartFragments) isnt ''
fragments = fragments.concat defPartFragments
fragments = [@makeCode(defPart)]
fragments.push @makeCode(resultPart) if resultPart
fragments = fragments.concat @makeCode(@tab), @makeCode( 'for ('),
forPartFragments, @makeCode(") {#{guardPart}#{varPart}"), bodyFragments,
forCode = if @await then 'for ' else 'for ('
forClose = if @await then '' else ')'
fragments = fragments.concat @makeCode(@tab), @makeCode( forCode),
forPartFragments, @makeCode("#{forClose} {#{guardPart}#{varPart}"), bodyFragments,
@makeCode(@tab), @makeCode('}')
fragments.push @makeCode(returnResult) if returnResult
fragments
pluckDirectCall: (o, body) ->
defs = []
for expr, idx in body.expressions
expr = expr.unwrapAll()
continue unless expr instanceof Call
val = expr.variable?.unwrapAll()
continue unless (val instanceof Code) or
(val instanceof Value and
val.base?.unwrapAll() instanceof Code and
val.properties.length is 1 and
val.properties[0].name?.value in ['call', 'apply'])
fn = val.base?.unwrapAll() or val
ref = new IdentifierLiteral o.scope.freeVariable 'fn'
base = new Value ref
if val.base
[val.base, base] = [base, val]
body.expressions[idx] = new Call base, expr.args
defs = defs.concat @makeCode(@tab), (new Assign(ref, fn).compileToFragments(o, LEVEL_TOP)), @makeCode(';\n')
defs
#### Switch
# A JavaScript *switch* statement. Converts into a returnable expression on-demand.
@@ -3755,13 +3787,7 @@ 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)) {
@@ -3769,19 +3795,6 @@ UTILITIES =
}
}
"
_extends: -> "
Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
}
"
# Shortcuts to speed up the lookup time for native functions.
hasProp: -> '{}.hasOwnProperty'

View File

@@ -30,6 +30,14 @@ replDefaults =
try
# Tokenize the clean input.
tokens = CoffeeScript.tokens input
# Filter out tokens generated just to hold comments.
if tokens.length >= 2 and tokens[0].generated and
tokens[0].comments?.length isnt 0 and tokens[0][1] is '' and
tokens[1][0] is 'TERMINATOR'
tokens = tokens[2...]
if tokens.length >= 1 and tokens[tokens.length - 1].generated and
tokens[tokens.length - 1].comments?.length isnt 0 and tokens[tokens.length - 1][1] is ''
tokens.pop()
# Collect referenced variable names just like in `CoffeeScript.compile`.
referencedVars = (token[1] for token in tokens when token[0] is 'IDENTIFIER')
# Generate the AST of the tokens.

View File

@@ -263,6 +263,20 @@ exports.Rewriter = class Rewriter
stack.pop()
start = stack.pop()
inControlFlow = =>
seenFor = @findTagsBackwards(i, ['FOR']) and @findTagsBackwards(i, ['FORIN', 'FOROF', 'FORFROM'])
controlFlow = seenFor or @findTagsBackwards i, ['WHILE', 'UNTIL', 'LOOP', 'LEADING_WHEN']
return no unless controlFlow
isFunc = no
tagCurrentLine = token[2].first_line
@detectEnd i,
(token, i) -> token[0] in LINEBREAKS
(token, i) ->
[prevTag, ,{first_line}] = tokens[i - 1] || []
isFunc = tagCurrentLine is first_line and prevTag in ['->', '=>']
returnOnNegativeLevel: yes
isFunc
# Recognize standard implicit calls like
# f a, f() b, f? c, h[0] d etc.
# Added support for spread dots on the left side: f ...a
@@ -271,7 +285,8 @@ exports.Rewriter = class Rewriter
(nextTag in IMPLICIT_CALL or
(nextTag is '...' and @tag(i + 2) in IMPLICIT_CALL and not @findTagsBackwards(i, ['INDEX_START', '['])) or
nextTag in IMPLICIT_UNSPACED_CALL and
not nextToken.spaced and not nextToken.newLine)
not nextToken.spaced and not nextToken.newLine) and
not inControlFlow()
tag = token[0] = 'FUNC_EXIST' if tag is '?'
startImplicitCall i + 1
return forward(2)
@@ -343,11 +358,14 @@ exports.Rewriter = class Rewriter
stackItem[2].sameLine = no if isImplicitObject stackItem
newLine = prevTag is 'OUTDENT' or prevToken.newLine
if tag in IMPLICIT_END or tag in CALL_CLOSERS and newLine
if tag in IMPLICIT_END or
(tag in CALL_CLOSERS and newLine) or
(tag in ['..', '...'] and @findTagsBackwards(i, ["INDEX_START"]))
while inImplicit()
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()
# Close implicit calls when reached end of argument list
if inImplicitCall() and prevTag isnt ','
if inImplicitCall() and prevTag isnt ',' or
(prevTag is ',' and tag is 'TERMINATOR' and not nextTag?)
endImplicitCall()
# Close implicit objects such as:
# return a: 1, b: 2 unless true
@@ -377,7 +395,7 @@ 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
if tag is ',' and not @looksObjectish(i + 1) and inImplicitObject() and not (@tag(i + 2) in ['FOROF', 'FORIN']) 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.
@@ -544,20 +562,49 @@ exports.Rewriter = class Rewriter
# blocks are added.
normalizeLines: ->
starter = indent = outdent = null
leading_switch_when = null
leading_if_then = null
# Count `THEN` tags
ifThens = []
condition = (token, i) ->
token[1] isnt ';' and token[0] in SINGLE_CLOSERS and
not (token[0] is 'TERMINATOR' and @tag(i + 1) in EXPRESSION_CLOSE) and
not (token[0] is 'ELSE' and starter isnt 'THEN') and
not (token[0] is 'ELSE' and
(starter isnt 'THEN' or (leading_if_then or leading_switch_when))) and
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>']) or
token[0] in CALL_CLOSERS and
(@tokens[i - 1].newLine or @tokens[i - 1][0] is 'OUTDENT')
action = (token, i) ->
ifThens.pop() if token[0] is 'ELSE' and starter is 'THEN'
@tokens.splice (if @tag(i - 1) is ',' then i - 1 else i), 0, outdent
closeElseTag = (tokens, i) =>
tlen = ifThens.length
return i unless tlen > 0
lastThen = ifThens.pop()
[, outdentElse] = @indentation tokens[lastThen]
# Insert `OUTDENT` to close inner `IF`.
outdentElse[1] = tlen*2
tokens.splice(i, 0, outdentElse)
# Insert `OUTDENT` to close outer `IF`.
outdentElse[1] = 2
tokens.splice(i + 1, 0, outdentElse)
# Remove outdents from the end.
@detectEnd i + 2,
(token, i) -> token[0] in ['OUTDENT', 'TERMINATOR']
(token, i) ->
if @tag(i) is 'OUTDENT' and @tag(i + 1) is 'OUTDENT'
tokens.splice i, 2
i + 2
@scanTokens (token, i, tokens) ->
[tag] = token
conditionTag = tag in ['->', '=>'] and
@findTagsBackwards(i, ['IF', 'WHILE', 'FOR', 'UNTIL', 'SWITCH', 'WHEN', 'LEADING_WHEN', '[', 'INDEX_START']) and
not (@findTagsBackwards i, ['THEN', '..', '...'])
if tag is 'TERMINATOR'
if @tag(i + 1) is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
tokens.splice i, 1, @indentation()...
@@ -574,10 +621,18 @@ exports.Rewriter = class Rewriter
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')
not (tag is 'ELSE' and @tag(i + 1) is 'IF') and
not conditionTag
starter = tag
[indent, outdent] = @indentation tokens[i]
indent.fromThen = true if starter is 'THEN'
if tag is 'THEN'
leading_switch_when = @findTagsBackwards(i, ['LEADING_WHEN']) and @tag(i + 1) is 'IF'
leading_if_then = @findTagsBackwards(i, ['IF']) and @tag(i + 1) is 'IF'
ifThens.push i if tag is 'THEN' and @findTagsBackwards(i, ['IF'])
# `ELSE` tag is not closed.
if tag is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
i = closeElseTag tokens, i
tokens.splice i + 1, 0, indent
@detectEnd i + 2, condition, action
tokens.splice i, 1 if tag is 'THEN'

View File

@@ -26,6 +26,94 @@ test "incorrect indentation without commas", ->
ok result[0][0] is 'a'
ok result[1]['b'] is 'c'
# Elisions
test "array elisions", ->
eq [,1].length, 2
eq [,,1,2,,].length, 5
arr = [1,,2]
eq arr.length, 3
eq arr[1], undefined
eq [,,].length, 2
test "array elisions indentation and commas", ->
arr1 = [
, 1, 2, , , 3,
4, 5, 6
, , 8, 9,
]
eq arr1.length, 12
eq arr1[5], 3
eq arr1[9], undefined
arr2 = [, , 1,
2, , 3,
, 4, 5
6
, , ,
]
eq arr2.length, 12
eq arr2[8], 5
eq arr2[1], undefined
test "array elisions destructuring", ->
arr = [1,2,3,4,5,6,7,8,9]
[,a] = arr
[,,,b] = arr
arrayEq [a,b], [2,4]
[,a,,b,,c,,,d] = arr
arrayEq [a,b,c,d], [2,4,6,9]
[
,e,
,f,
,g,
,,h] = arr
arrayEq [e,f,g,h], [2,4,6,9]
test "array elisions destructuring with splats and expansions", ->
arr = [1,2,3,4,5,6,7,8,9]
[,a,,,b...] = arr
arrayEq [a,b], [2,[5,6,7,8,9]]
[,c,...,,d,,e] = arr
arrayEq [c,d,e], [2,7,9]
[...,f,,,g,,,] = arr
arrayEq [f,g], [4,7]
test "array elisions as function parameters", ->
arr = [1,2,3,4,5,6,7,8,9]
foo = ([,a]) -> a
a = foo arr
eq a, 2
foo = ([,,,a]) -> a
a = foo arr
eq a, 4
foo = ([,a,,b,,c,,,d]) -> [a,b,c,d]
[a,b,c,d] = foo arr
arrayEq [a,b,c,d], [2,4,6,9]
test "array elisions nested destructuring", ->
arr = [
1,
[2,3, [4,5,6, [7,8,9] ] ]
]
[,a] = arr
arrayEq a[2][3], [7,8,9]
[,[,,[,b,,[,,c]]]] = arr
eq b, 5
eq c, 9
aobj = [
{},
{x: 2},
{},
[
{},
{},
{z:1, w:[1,2,4], p:3, q:4}
{},
{}
]
]
[,d,,[,,{w}]] = aobj
deepEqual d, {x:2}
arrayEq w, [1,2,4]
# Splats in Array Literals

View File

@@ -178,6 +178,13 @@ test "#713: destructuring assignment should return right-hand-side value", ->
eq nonceB, b
eq nonceB, d
test "#4787 destructuring of objects within arrays", ->
arr = [1, {a:1, b:2}]
[...,{a, b}] = arr
eq a, 1
eq b, arr[1].b
deepEqual {a, b}, arr[1]
test "destructuring assignment with splats", ->
a = {}; b = {}; c = {}; d = {}; e = {}
[x,y...,z] = [a,b,c,d,e]
@@ -246,311 +253,6 @@ 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, {}
# Should not trigger implicit call, e.g. rest ... => rest(...)
{
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
o = {
props: {
p: {
n: 1
m: 5
}
s: 6
}
}
{p: {m, q..., t = {obj...}}, r...} = o.props
eq m, o.props.p.m
deepEqual r, s: 6
deepEqual q, n: 1
deepEqual t, obj
@props = o.props
{p: {m}, r...} = @props
eq m, @props.p.m
deepEqual r, s: 6
{p: {m}, r...} = {o.props..., p:{m:9}}
eq m, 9
# Should not trigger implicit call, e.g. rest ... => rest(...)
{
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 }
# Should not trigger implicit call, e.g. rest ... => rest(...)
{ x, y, z ... } = { x: 1, y: 2, a: 3, b: 4 }
eq x, 1
eq y, 2
deepEqual z, { a: 3, b: 4 }
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
{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'
# Should not trigger implicit call, e.g. rest ... => rest(...)
{
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
(({
a
b
r ...
}) ->
eq 1, a
deepEqual r, {c: 3, d: 44, e: 55}
) {
obj2 ...
d: 44
e: 55
}
# Should not trigger implicit call, e.g. rest ... => rest(...)
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
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}}
# Should not trigger implicit call, e.g. rest ... => rest(...)
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}}
obj =
a:
b:
c:
d:
e: {}
obj9 = {a:1, obj.a.b.c..., g:3}
deepEqual obj9.d, {e: {}}
a = "a"
c = "c"
obj9 = {a:1, obj[a].b[c]..., g:3}
deepEqual obj9.d, {e: {}}
obj9 = {a:1, obj.a["b"].c["d"]..., g:3}
deepEqual obj9["e"], {}
test "bracket insertion when necessary", ->
[a] = [0] ? [1]
eq a, 0
@@ -898,32 +600,124 @@ test "#4566: destructuring with nested default values", ->
{e: {f = 5} = {}} = {}
eq 5, f
test "#4674: _extends utility for object spreads 1", ->
eqJS(
"{a, b..., c..., d}"
"""
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
test "#4878: Compile error when using destructuring with a splat or expansion in an array", ->
arr = ['a', 'b', 'c', 'd']
_extends({a}, b, c, {d});
"""
)
f1 = (list) ->
[first, ..., last] = list
test "#4674: _extends utility for object spreads 2", ->
_extends = -> 3
a = b: 1
c = d: 2
e = {a..., c...}
eq e.b, 1
eq e.d, 2
f2 = (list) ->
[first..., last] = list
test "#4673: complex destructured object spread variables", ->
b = c: 1
{{a...}...} = b
eq a.c, 1
f3 = (list) ->
([first, ...] = list); first
d = {}
{d.e...} = f: 1
eq d.e.f, 1
f4 = (list) ->
([first, rest...] = list); rest
{{g}...} = g: 1
eq g, 1
arrayEq f1(arr), arr
arrayEq f2(arr), arr
arrayEq f3(arr), 'a'
arrayEq f4(arr), ['b', 'c', 'd']
foo = (list) ->
ret =
if list?.length > 0
[first, ..., last] = list
[first, last]
else
[]
arrayEq foo(arr), ['a', 'd']
bar = (list) ->
ret =
if list?.length > 0
[first, rest...] = list
[first, rest]
else
[]
arrayEq bar(arr), ['a', ['b', 'c', 'd']]
test "destructuring assignment with an empty array in object", ->
obj =
a1: [1, 2]
b1: 3
{a1:[], b1} = obj
eq 'undefined', typeof a1
eq b1, 3
obj =
a2:
b2: [1, 2]
c2: 3
{a2: {b2:[]}, c2} = obj
eq 'undefined', typeof b2
eq c2, 3
test "#5004: array destructuring with accessors", ->
obj =
arr: ['a', 'b', 'c', 'd']
list: {}
f1: ->
[@first, @rest...] = @arr
f2: ->
[@second, @third..., @last] = @rest
f3: ->
[@list.a, @list.middle..., @list.d] = @arr
obj.f1()
eq obj.first, 'a'
arrayEq obj.rest, ['b', 'c', 'd']
obj.f2()
eq obj.second, 'b'
arrayEq obj.third, ['c']
eq obj.last, 'd'
obj.f3()
eq obj.list.a, 'a'
arrayEq obj.list.middle, ['b', 'c']
eq obj.list.d, 'd'
[obj.list.middle..., d] = obj.arr
eq d, 'd'
arrayEq obj.list.middle, ['a', 'b', 'c']
test "#4884: destructured object splat", ->
[{length}...] = [1, 2, 3]
eq length, 3
[{length: len}...] = [1, 2, 3]
eq len, 3
[{length}..., three] = [1, 2, 3]
eq length, 2
eq three, 3
[{length: len}..., three] = [1, 2, 3]
eq len, 2
eq three, 3
x = [{length}..., three] = [1, 2, 3]
eq length, 2
eq three, 3
eq x[2], 3
x = [{length: len}..., three] = [1, 2, 3]
eq len, 2
eq three, 3
eq x[2], 3
test "#4884: destructured array splat", ->
[[one, two, three]...] = [1, 2, 3]
eq one, 1
eq two, 2
eq three, 3
[[one, two]..., three] = [1, 2, 3]
eq one, 1
eq two, 2
eq three, 3
x = [[one, two]..., three] = [1, 2, 3]
eq one, 1
eq two, 2
eq three, 3
eq x[2], 3

View File

@@ -1,8 +1,9 @@
# Functions that contain the `await` keyword will compile into async functions,
# supported by Node 7.6+, Chrome 55+, Firefox 52+, Safari 10.1+ and Edge.
# But runtimes that dont support the `await` keyword will throw an error,
# even if we put `return unless global.supportsAsync` at the top of this file.
# Therefore we need to prevent runtimes which will choke on such code from even
# But runtimes that dont support the `await` keyword will throw an error just
# from parsing this file, even without executing it, even if we put
# `return unless try new Function 'async () => {}'` at the top of this file.
# Therefore we need to prevent runtimes which will choke on such code from
# parsing it, which is handled in `Cakefile`.

View File

@@ -0,0 +1,32 @@
# This is always fulfilled.
winLater = (val, ms) ->
new Promise (resolve) -> setTimeout (-> resolve val), ms
# This is always rejected.
failLater = (val, ms) ->
new Promise (resolve, reject) -> setTimeout (-> reject new Error val), ms
createAsyncIterable = (syncIterable) ->
for elem in syncIterable
yield await winLater elem, 50
test "async iteration", ->
foo = (x for await x from createAsyncIterable [1,2,3])
arrayEq foo, [1, 2, 3]
test "async generator functions", ->
foo = (val) ->
yield await winLater val + 1, 50
bar = (val) ->
yield await failLater val - 1, 50
a = await foo(41).next()
eq a.value, 42
try
b = do -> await bar(41).next()
b.catch (err) ->
eq "40", err.message
catch err
ok no

View File

@@ -266,6 +266,7 @@ test "classes with value'd constructors", ->
inner = ++counter
->
@value = inner
@
class One
constructor: classMaker()
@@ -1857,3 +1858,45 @@ test "#4724: backticked expression in a class body with hoisted member", ->
a = new A
eq 42, a.x
eq 84, a.hoisted
test "#4822: nested anonymous classes use non-conflicting variable names", ->
Class = class
@a: class
@b: 1
eq Class.a.b, 1
test "#4827: executable class body wrappers have correct context", ->
test = ->
class @A
class @B extends @A
@property = 1
o = {}
test.call o
ok typeof o.A is typeof o.B is 'function'
test "#4868: Incorrect Cant call super with @params error", ->
class A
constructor: (@func = ->) ->
@x = 1
@func()
class B extends A
constructor: ->
super -> @x = 2
a = new A
b = new B
eq 1, a.x
eq 2, b.x
class C
constructor: (@c = class) -> @c
class D extends C
constructor: ->
super class then constructor: (@a) -> @a = 3
d = new (new D).c
eq 3, d.a

View File

@@ -364,7 +364,7 @@ test "#3132: Place block-comments nicely", ->
return DummyClass;
})();"""
}).call(this);"""
test "#3638: Demand a whitespace after # symbol", ->
eqJS """

View File

@@ -170,3 +170,10 @@ test "using transpile from the Node API requires an object", ->
test "transpile option applies to imported .coffee files", ->
return if global.testingBrowser
doesNotThrow -> transpile 'run', "import { getSep } from './test/importing/transpile_import'\ngetSep()"
test "#3306: trailing comma in a function call in the last line", ->
eqJS '''
foo bar,
''', '''
foo(bar);
'''

View File

@@ -484,3 +484,828 @@ test "#4267: lots of for-loops in the same scope", ->
true
"""
ok CoffeeScript.eval(code)
# Test for issue #2342: Lexer: Inline `else` binds to wrong `if`/`switch`
test "#2343: if / then / if / then / else", ->
a = b = yes
c = e = g = no
d = 1
f = 2
h = 3
i = 4
s = ->
if a
if b
if c
d
else
if e
f
else
if g
h
else
i
t = ->
if a then if b
if c then d
else if e
f
else if g
h
else
i
u = ->
if a then if b
if c then d else if e
f
else if g
h
else i
v = ->
if a then if b
if c then d else if e then f
else if g then h
else i
w = ->
if a then if b
if c then d
else if e
f
else
if g then h
else i
x = -> if a then if b then if c then d else if e then f else if g then h else i
y = -> if a then if b then (if c then d else (if e then f else (if g then h else i)))
eq 4, s()
eq 4, t()
eq 4, u()
eq 4, v()
eq 4, w()
eq 4, x()
eq 4, y()
c = yes
eq 1, s()
eq 1, t()
eq 1, u()
eq 1, v()
eq 1, w()
eq 1, x()
eq 1, y()
b = no
eq undefined, s()
eq undefined, t()
eq undefined, u()
eq undefined, v()
eq undefined, w()
eq undefined, x()
eq undefined, y()
test "#2343: if / then / if / then / else / else", ->
a = b = yes
c = e = g = no
d = 1
f = 2
h = 3
i = 4
j = 5
k = 6
s = ->
if a
if b
if c
d
else
e
if e
f
else
if g
h
else
i
else
j
else
k
t = ->
if a
if b
if c then d
else if e
f
else if g
h
else
i
else
j
else
k
u = ->
if a
if b
if c then d else if e
f
else if g
h
else i
else j
else k
v = ->
if a
if b
if c then d else if e then f
else if g then h
else i
else j else k
w = ->
if a then if b
if c then d
else if e
f
else
if g then h
else i
else j else k
x = -> if a then if b then if c then d else if e then f else if g then h else i else j else k
y = -> if a then (if b then (if c then d else (if e then f else (if g then h else i))) else j) else k
eq 4, s()
eq 4, t()
eq 4, u()
eq 4, v()
eq 4, w()
eq 4, x()
eq 4, y()
c = yes
eq 1, s()
eq 1, t()
eq 1, u()
eq 1, v()
eq 1, w()
eq 1, x()
eq 1, y()
b = no
eq 5, s()
eq 5, t()
eq 5, u()
eq 5, v()
eq 5, w()
eq 5, x()
eq 5, y()
a = no
eq 6, s()
eq 6, t()
eq 6, u()
eq 6, v()
eq 6, w()
eq 6, x()
eq 6, y()
test "#2343: switch / when / then / if / then / else", ->
a = b = yes
c = e = g = no
d = 1
f = 2
h = 3
i = 4
s = ->
switch
when a
if b
if c
d
else
if e
f
else
if g
h
else
i
t = ->
switch
when a then if b
if c then d
else if e
f
else if g
h
else
i
u = ->
switch
when a then if b then if c then d
else if e then f
else if g then h else i
v = ->
switch
when a then if b then if c then d else if e then f
else if g then h else i
w = ->
switch
when a then if b then if c then d else if e then f
else if g
h
else i
x = ->
switch
when a then if b then if c then d else if e then f else if g then h else i
y = -> switch
when a then if b then (if c then d else (if e then f else (if g then h else i)))
eq 4, s()
eq 4, t()
eq 4, u()
eq 4, v()
eq 4, w()
eq 4, x()
eq 4, y()
c = yes
eq 1, s()
eq 1, t()
eq 1, u()
eq 1, v()
eq 1, w()
eq 1, x()
eq 1, y()
b = no
eq undefined, s()
eq undefined, t()
eq undefined, u()
eq undefined, v()
eq undefined, w()
eq undefined, x()
eq undefined, y()
test "#2343: switch / when / then / if / then / else / else", ->
a = b = yes
c = e = g = no
d = 1
f = 2
h = 3
i = 4
s = ->
switch
when a
if b
if c
d
else if e
f
else if g
h
else
i
else
0
t = ->
switch
when a
if b
if c then d
else if e
f
else if g
h
else i
else 0
u = ->
switch
when a
if b then if c
d
else if e
f
else if g
h
else i
else 0
v = ->
switch
when a
if b then if c then d
else if e
f
else if g
h
else i
else 0
w = ->
switch
when a
if b then if c then d
else if e then f
else if g then h
else i
else 0
x = ->
switch
when a
if b then if c then d else if e then f else if g then h else i
else 0
y = -> switch
when a
if b then (if c then d else (if e then f else (if g then h else i)))
else 0
eq 4, s()
eq 4, t()
eq 4, u()
eq 4, v()
eq 4, w()
eq 4, x()
eq 4, y()
c = yes
eq 1, s()
eq 1, t()
eq 1, u()
eq 1, v()
eq 1, w()
eq 1, x()
eq 1, y()
b = no
eq undefined, s()
eq undefined, t()
eq undefined, u()
eq undefined, v()
eq undefined, w()
eq undefined, x()
eq undefined, y()
b = yes
a = no
eq 0, s()
eq 0, t()
eq 0, u()
eq 0, v()
eq 0, w()
eq 0, x()
eq 0, y()
test "#2343: switch / when / then / if / then / else / else / else", ->
a = b = yes
c = e = g = no
d = 1
f = 2
h = 3
i = 4
j = 5
s = ->
switch
when a
if b
if c
d
else if e
f
else if g
h
else
i
else
j
else
0
t = ->
switch
when a
if b
if c then d
else if e
f
else if g
h
else i
else
j
else 0
u = ->
switch
when a
if b
if c
d
else if e
f
else if g
h
else i
else j
else 0
v = ->
switch
when a
if b
if c then d
else if e
f
else if g then h
else i
else j
else 0
w = ->
switch
when a
if b
if c then d
else if e then f
else if g then h
else i
else j
else 0
x = ->
switch
when a
if b then if c then d else if e then f else if g then h else i else j
else 0
y = -> switch
when a
if b then (if c then d else (if e then f else (if g then h else i))) else j
else 0
eq 4, s()
eq 4, t()
eq 4, u()
eq 4, v()
eq 4, w()
eq 4, x()
eq 4, y()
c = yes
eq 1, s()
eq 1, t()
eq 1, u()
eq 1, v()
eq 1, w()
eq 1, x()
eq 1, y()
b = no
eq 5, s()
eq 5, t()
eq 5, u()
eq 5, v()
eq 5, w()
eq 5, x()
eq 5, y()
b = yes
a = no
eq 0, s()
eq 0, t()
eq 0, u()
eq 0, v()
eq 0, w()
eq 0, x()
eq 0, y()
# Test for issue #3921: Inline function without parentheses used in condition fails to compile
test "#3921: `if` & `unless`", ->
a = {}
eq a, if do -> no then undefined else a
a1 = undefined
if do -> yes
a1 = a
eq a, a1
b = {}
eq b, unless do -> no then b else undefined
b1 = undefined
unless do -> no
b1 = b
eq b, b1
c = 0
if (arg = undefined) -> yes then c++
eq 1, c
d = 0
if (arg = undefined) -> yes
d++
eq 1, d
answer = 'correct'
eq answer, if do -> 'wrong' then 'correct' else 'wrong'
eq answer, unless do -> no then 'correct' else 'wrong'
statm1 = undefined
if do -> 'wrong'
statm1 = 'correct'
eq answer, statm1
statm2 = undefined
unless do -> no
statm2 = 'correct'
eq answer, statm2
test "#3921: `post if`", ->
a = {}
eq a, a unless do -> no
a1 = a if do -> yes
eq a, a1
c = 0
c++ if (arg = undefined) -> yes
eq 1, c
d = 0
d++ if (arg = undefined) -> yes
eq 1, d
answer = 'correct'
eq answer, 'correct' if do -> 'wrong'
eq answer, 'correct' unless do -> not 'wrong'
statm1 = undefined
statm1 = 'correct' if do -> 'wrong'
eq answer, statm1
statm2 = undefined
statm2 = 'correct' unless do -> not 'wrong'
eq answer, statm2
test "Issue 3921: `while` & `until`", ->
i = 5
assert = (a) -> ok 5 > a > 0
result1 = while do (num = 1) -> i -= num
assert i
i
ok result1.join(' ') is '4 3 2 1'
j = 5
result2 = until do (num = 1) -> (j -= num) < 1
assert j
j
ok result2.join(' ') is '4 3 2 1'
test "#3921: `switch`", ->
i = 1
a = switch do (m = 2) -> i * m
when 5 then "five"
when 4 then "four"
when 3 then "three"
when 2 then "two"
when 1 then "one"
else "none"
eq "two", a
j = 12
b = switch do (m = 3) -> j / m
when 5 then "five"
when 4 then "four"
when 3 then "three"
when 2 then "two"
when 1 then "one"
else "none"
eq "four", b
k = 20
c = switch do (m = 4) -> k / m
when 5 then "five"
when 4 then "four"
when 3 then "three"
when 2 then "two"
when 1 then "one"
else "none"
eq "five", c
# Issue #3909: backslash to break line in `for` loops throw syntax error
test "#3909: backslash `for own ... of`", ->
obj = {a: 1, b: 2, c: 3}
arr = ['a', 'b', 'c']
x1 \
= ( key for own key of obj )
arrayEq x1, arr
x2 = \
( key for own key of obj )
arrayEq x2, arr
x3 = ( \
key for own key of obj )
arrayEq x3, arr
x4 = ( key \
for own key of obj )
arrayEq x4, arr
x5 = ( key for own key of \
obj )
arrayEq x5, arr
x6 = ( key for own key of obj \
)
arrayEq x6, arr
x7 = ( key for \
own key of obj )
arrayEq x7, arr
x8 = ( key for own \
key of obj )
arrayEq x8, arr
x9 = ( key for own key \
of obj )
arrayEq x9, arr
test "#3909: backslash `for ... of`", ->
obj = {a: 1, b: 2, c: 3}
arr = ['a', 'b', 'c']
x1 \
= ( key for key of obj )
arrayEq x1, arr
x2 = \
( key for key of obj )
arrayEq x2, arr
x3 = ( \
key for key of obj )
arrayEq x3, arr
x4 = ( key \
for key of obj )
arrayEq x4, arr
x5 = ( key for key of \
obj )
arrayEq x5, arr
x6 = ( key for key of obj \
)
arrayEq x6, arr
x7 = ( key for \
key of obj )
arrayEq x7, arr
x8 = ( key for key \
of obj )
arrayEq x8, arr
test "#3909: backslash `for ... in`", ->
arr = ['a', 'b', 'c']
x1 \
= ( key for key in arr )
arrayEq x1, arr
x2 = \
( key for key in arr )
arrayEq x2, arr
x3 = ( \
key for key in arr )
arrayEq x3, arr
x4 = ( key \
for key in arr )
arrayEq x4, arr
x5 = ( key for key in \
arr )
arrayEq x5, arr
x6 = ( key for key in arr \
)
arrayEq x6, arr
x7 = ( key for \
key in arr )
arrayEq x7, arr
x8 = ( key for key \
in arr )
arrayEq x8, arr
test "#4871: `else if` no longer output together ", ->
eqJS '''
if a then b else if c then d else if e then f else g
''',
'''
if (a) {
b;
} else if (c) {
d;
} else if (e) {
f;
} else {
g;
}
'''
eqJS '''
if no
1
else if yes
2
''',
'''
if (false) {
1;
} else if (true) {
2;
}
'''
test "#4898: Lexer: backslash line continuation is inconsistent", ->
if ( \
false \
or \
true \
)
a = 42
eq a, 42
if ( \
false \
or \
true \
)
b = 42
eq b, 42
if ( \
false \
or \
true \
)
c = 42
eq c, 42
if \
false \
or \
true
d = 42
eq d, 42
if \
false or \
true
e = 42
eq e, 42
if \
false or \
true \
then \
f = 42 \
else
f = 24
eq f, 42

View File

@@ -742,3 +742,47 @@ test '#4686: comments inside interpolations that also contain CSX attributes', -
</div>;
'''
# https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
test 'JSX fragments: empty fragment', ->
eqJS '''
<></>
''', '''
<></>;
'''
test 'JSX fragments: fragment with text nodes', ->
eqJS '''
<>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</>
''', '''
<>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</>;
'''
test 'JSX fragments: fragment with component nodes', ->
eqJS '''
Component = (props) =>
<Fragment>
<OtherComponent />
<OtherComponent />
</Fragment>
''', '''
var Component;
Component = (props) => {
return <Fragment>
<OtherComponent />
<OtherComponent />
</Fragment>;
};
'''

View File

@@ -183,9 +183,9 @@ test "#1096: unexpected generated tokens", ->
for i in [1]:
1
''', '''
[stdin]:1:10: error: unexpected [
for i in [1]:
^
[stdin]:2:4: error: unexpected end of input
1
^
'''
# Unexpected regex
assertErrorFormat '{/a/i: val}', '''
@@ -808,29 +808,8 @@ test "invalid numbers", ->
^^^
'''
test "unexpected object keys", ->
assertErrorFormat '''
{[[]]}
''', '''
[stdin]:1:2: error: unexpected [
{[[]]}
^
'''
assertErrorFormat '''
{[[]]: 1}
''', '''
[stdin]:1:2: error: unexpected [
{[[]]: 1}
^
'''
assertErrorFormat '''
[[]]: 1
''', '''
[stdin]:1:1: error: unexpected [
[[]]: 1
^
'''
test "unexpected object keys", ->
assertErrorFormat '''
{(a + "b")}
''', '''
@@ -852,20 +831,6 @@ test "unexpected object keys", ->
(a + "b"): 1
^
'''
assertErrorFormat '''
a: 1, [[]]: 2
''', '''
[stdin]:1:7: error: unexpected [
a: 1, [[]]: 2
^
'''
assertErrorFormat '''
{a: 1, [[]]: 2}
''', '''
[stdin]:1:8: error: unexpected [
{a: 1, [[]]: 2}
^
'''
test "invalid object keys", ->
assertErrorFormat '''
@@ -997,6 +962,42 @@ test "#4097: `yield return` as an expression", ->
^^^^^^^^^^^^
'''
test "#5013: `await return` as an expression", ->
assertErrorFormat '''
-> (await return)
''', '''
[stdin]:1:5: error: cannot use a pure statement in an expression
-> (await return)
^^^^^^^^^^^^
'''
test "#5013: `return` as an expression", ->
assertErrorFormat '''
-> (return)
''', '''
[stdin]:1:5: error: cannot use a pure statement in an expression
-> (return)
^^^^^^
'''
test "#5013: `break` as an expression", ->
assertErrorFormat '''
(b = 1; break) for b in a
''', '''
[stdin]:1:9: error: cannot use a pure statement in an expression
(b = 1; break) for b in a
^^^^^
'''
test "#5013: `continue` as an expression", ->
assertErrorFormat '''
(b = 1; continue) for b in a
''', '''
[stdin]:1:9: error: cannot use a pure statement in an expression
(b = 1; continue) for b in a
^^^^^^^^
'''
test "`&&=` and `||=` with a space in-between", ->
assertErrorFormat '''
a = 0
@@ -1197,6 +1198,18 @@ test "bound functions cannot be generators", ->
^^^^^^^^^^
'''
test "#4790: bound functions cannot be generators, even when were creating IIFEs", ->
assertErrorFormat '''
=>
for x in []
for y in []
yield z
''', '''
[stdin]:4:7: error: yield cannot occur inside bound (fat arrow) functions
yield z
^^^^^^^
'''
test "CoffeeScript keywords cannot be used as unaliased names in import lists", ->
assertErrorFormat """
import { unless, baz as bar } from 'lib'
@@ -1217,28 +1230,6 @@ test "CoffeeScript keywords cannot be used as local names in import list aliases
^^^^^^
'''
test "function cannot contain both `await` and `yield`", ->
assertErrorFormat '''
f = () ->
yield 5
await a
''', '''
[stdin]:3:3: error: function can't contain both yield and await
await a
^^^^^^^
'''
test "function cannot contain both `await` and `yield from`", ->
assertErrorFormat '''
f = () ->
yield from a
await b
''', '''
[stdin]:3:3: error: function can't contain both yield and await
await b
^^^^^^^
'''
test "cannot have `await` outside a function", ->
assertErrorFormat '''
await 1
@@ -1791,3 +1782,62 @@ test "#3098: suppressed newline should be unsuppressed by semicolon", ->
a = ; 5
^
'''
test "#4811: '///' inside a heregex comment does not close the heregex", ->
assertErrorFormat '''
/// .* # comment ///
''', '''
[stdin]:1:1: error: missing ///
/// .* # comment ///
^^^
'''
test "#3933: prevent implicit calls when cotrol flow is missing `THEN`", ->
assertErrorFormat '''
for a in b do ->
''','''
[stdin]:1:12: error: unexpected do
for a in b do ->
^^
'''
assertErrorFormat '''
for a in b ->
''','''
[stdin]:1:12: error: unexpected ->
for a in b ->
^^
'''
assertErrorFormat '''
for a in b do =>
''','''
[stdin]:1:12: error: unexpected do
for a in b do =>
^^
'''
assertErrorFormat '''
while a do ->
''','''
[stdin]:1:9: error: unexpected do
while a do ->
^^
'''
assertErrorFormat '''
until a do =>
''','''
[stdin]:1:9: error: unexpected do
until a do =>
^^
'''
assertErrorFormat '''
switch
when a ->
''','''
[stdin]:2:10: error: unexpected ->
when a ->
^^
'''

View File

@@ -8,7 +8,7 @@ nonce = {}
# Throw
test "basic exception throwing", ->
throws (-> throw 'error'), 'error'
throws (-> throw 'error'), /^error$/
# Empty Try/Catch/Finally

View File

@@ -0,0 +1,19 @@
# The `**` and `**=` operators are only supported in Node 7.5+, so the tests
# for these exponentiation operators are split out into their own file to be
# loaded only by supported runtimes.
test "exponentiation operator", ->
eq 27, 3 ** 3
test "exponentiation operator has higher precedence than other maths operators", ->
eq 55, 1 + 3 ** 3 * 2
eq -4, -2 ** 2
eq 0, (!2) ** 2
test "exponentiation operator is right associative", ->
eq 2, 2 ** 1 ** 3
test "exponentiation operator compound assignment", ->
a = 2
a **= 3
eq 8, a

View File

@@ -329,6 +329,15 @@ test "Simple Destructuring function arguments with same-named variables in scope
eq f([2]), 2
eq x, 1
test "#4843: Bad output when assigning to @prop in destructuring assignment with defaults", ->
works = "maybe"
drinks = "beer"
class A
constructor: ({@works = 'no', @drinks = 'wine'}) ->
a = new A {works: 'yes', drinks: 'coffee'}
eq a.works, 'yes'
eq a.drinks, 'coffee'
test "caching base value", ->
obj =
@@ -795,7 +804,7 @@ test "get and set can be used as class method names", ->
eq 4, B.get()
eq 5, B.set()
test "functions named get or set can be used without parentheses when attached to an object; #4524", ->
test "#4524: functions named get or set can be used without parentheses when attached to an object", ->
obj =
get: (x) -> x + 2
set: (x) -> x + 3
@@ -814,6 +823,8 @@ test "functions named get or set can be used without parentheses when attached t
eq 12, obj.get 10
eq 13, obj.set 10
eq 12, obj?.get 10
eq 13, obj?.set 10
eq 14, a.get 10
eq 15, a.set 10
@@ -836,3 +847,59 @@ test "functions named get or set can be used without parentheses when attached t
eq 16, b.get value: @ten
eq 17, b.set value: @ten
test "#4836: functions named get or set can be used without parentheses when attached to this or @", ->
@get = (x) -> x + 2
@set = (x) -> x + 3
@a = 4
eq 12, this.get 10
eq 13, this.set 10
eq 12, this?.get 10
eq 13, this?.set 10
eq 6, this.get @a
eq 7, this.set @a
eq 6, this?.get @a
eq 7, this?.set @a
eq 12, @get 10
eq 13, @set 10
eq 12, @?.get 10
eq 13, @?.set 10
eq 6, @get @a
eq 7, @set @a
eq 6, @?.get @a
eq 7, @?.set @a
test "#4852: functions named get or set can be used without parentheses when attached to this or @, with an argument of an implicit object", ->
@get = ({ x }) -> x + 2
@set = ({ x }) -> x + 3
eq 12, @get x: 10
eq 13, @set x: 10
eq 12, @?.get x: 10
eq 13, @?.set x: 10
eq 12, this?.get x: 10
eq 13, this?.set x: 10
test "#4473: variable scope in chained calls", ->
obj =
foo: -> this
bar: (a) ->
a()
this
obj.foo(a = 1).bar(-> a = 2)
eq a, 2
obj.bar(-> b = 2).foo(b = 1)
eq b, 1
obj.foo(c = 1).bar(-> c = 2).foo(c = 3)
eq c, 3
obj.foo([d, e] = [1, 2]).bar(-> d = 4)
eq d, 4
obj.foo({f} = {f: 1}).bar(-> f = 5)
eq f, 5

View File

@@ -127,6 +127,16 @@ test "destructured splatted parameters", ->
splatArray = ([a ...]) -> a
splatArrayRest = ([a ...],b ...) -> arrayEq(a,b); b
test "#4884: object-destructured splatted parameters", ->
f = ({length}...) -> length
eq f(4, 5, 6), 3
f = ({length: len}...) -> len
eq f(4, 5, 6), 3
f = ({length}..., last) -> [length, last]
arrayEq f(4, 5, 6), [2, 6]
f = ({length: len}..., last) -> [len, last]
arrayEq f(4, 5, 6), [2, 6]
test "@-parameters: automatically assign an argument's value to a property of the context", ->
nonce = {}
@@ -215,86 +225,6 @@ 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
# Should not trigger implicit call, e.g. rest ... => rest(...)
(({
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}
f = ({ a: aa = 0, b: bb = 0 }) -> [aa, bb]
deepEqual [0, 0], f {}
deepEqual [0, 42], f {b:42}
deepEqual [42, 0], f {a:42}
deepEqual [42, 43], f {a:42, b:43}
test "#4005: `([a = {}]..., b) ->` weirdness", ->
fn = ([a = {}]..., b) -> [a, b]
deepEqual fn(5), [{}, 5]
@@ -550,15 +480,6 @@ test "#4413: expressions in function parameters that create generated variables
eq f(), 33
eq g(), 34
test "#4673: complex destructured object spread variables", ->
f = ({{a...}...}) ->
a
eq f(c: 1).c, 1
g = ({@y...}) ->
eq @y.b, 1
g b: 1
test "#4657: destructured array param declarations", ->
a = 1
b = 2

View File

@@ -168,7 +168,7 @@ test "`throw` can be yielded", ->
throws -> x.next()
test "symbolic operators has precedence over the `yield`", ->
symbolic = '+ - * / << >> & | || && ** ^ // or and'.split ' '
symbolic = '+ - * / << >> & | || && ^ // or and'.split ' '
compound = ("#{op}=" for op in symbolic)
relations = '< > == != <= >= is isnt'.split ' '

View File

@@ -139,7 +139,7 @@ eq 'multiline nested "interpolations" work', """multiline #{
eq 'function(){}', "#{->}".replace /\s/g, ''
ok /^a[\s\S]+b$/.test "a#{=>}b"
ok /^a[\s\S]+b$/.test "a#{ (x) -> x ** 2 }b"
ok /^a[\s\S]+b$/.test "a#{ (x) -> x %% 2 }b"
# Regular Expression Interpolation

View File

@@ -6,6 +6,8 @@ path = require 'path'
# Get the folder containing the compiled `coffee` executable and make it the
# PATH so that `#!/usr/bin/env coffee` resolves to our locally built file.
coffeeBinFolder = path.dirname require.resolve '../bin/coffee'
# For some reason, Windows requires `coffee` to be executed as `node coffee`.
coffeeCommand = if isWindows() then 'node coffee' else 'coffee'
spawnOptions =
cwd: coffeeBinFolder
encoding: 'utf8'
@@ -18,7 +20,7 @@ initialSpaceScript = require.resolve './importing/shebang_initial_space.coffee'
extraArgsScript = require.resolve './importing/shebang_extra_args.coffee'
initialSpaceExtraArgsScript = require.resolve './importing/shebang_initial_space_extra_args.coffee'
test "parse arguments for shebang scripts correctly (on unix platforms)", ->
test "parse arguments for shebang scripts correctly (on *nix platforms)", ->
return if isWindows()
stdout = execFileSync shebangScript, ['-abck'], spawnOptions
@@ -32,7 +34,7 @@ test "parse arguments for shebang scripts correctly (on unix platforms)", ->
arrayEq expectedArgs, realArgs
test "warn and remove -- if it is the second positional argument", ->
result = spawnSync 'coffee', [shebangScript, '--'], spawnOptions
result = spawnSync coffeeCommand, [shebangScript, '--'], spawnOptions
stderr = result.stderr.toString()
arrayEq JSON.parse(result.stdout), ['coffee', shebangScript]
ok stderr.match /^coffee was invoked with '--'/m
@@ -40,7 +42,7 @@ test "warn and remove -- if it is the second positional argument", ->
arrayEq JSON.parse(posArgs), [shebangScript, '--']
ok result.status is 0
result = spawnSync 'coffee', ['-b', shebangScript, '--'], spawnOptions
result = spawnSync coffeeCommand, ['-b', shebangScript, '--'], spawnOptions
stderr = result.stderr.toString()
arrayEq JSON.parse(result.stdout), ['coffee', shebangScript]
ok stderr.match /^coffee was invoked with '--'/m
@@ -49,7 +51,7 @@ test "warn and remove -- if it is the second positional argument", ->
ok result.status is 0
result = spawnSync(
'coffee', ['-b', shebangScript, '--', 'ANOTHER'], spawnOptions)
coffeeCommand, ['-b', shebangScript, '--', 'ANOTHER'], spawnOptions)
stderr = result.stderr.toString()
arrayEq JSON.parse(result.stdout), ['coffee', shebangScript, 'ANOTHER']
ok stderr.match /^coffee was invoked with '--'/m
@@ -58,7 +60,7 @@ test "warn and remove -- if it is the second positional argument", ->
ok result.status is 0
result = spawnSync(
'coffee', ['--', initialSpaceScript, 'arg'], spawnOptions)
coffeeCommand, ['--', initialSpaceScript, 'arg'], spawnOptions)
expectedArgs = ['coffee', initialSpaceScript, 'arg']
realArgs = JSON.parse result.stdout
arrayEq expectedArgs, realArgs
@@ -66,7 +68,7 @@ test "warn and remove -- if it is the second positional argument", ->
ok result.status is 0
test "warn about non-portable shebang lines", ->
result = spawnSync 'coffee', [extraArgsScript, 'arg'], spawnOptions
result = spawnSync coffeeCommand, [extraArgsScript, 'arg'], spawnOptions
stderr = result.stderr.toString()
arrayEq JSON.parse(result.stdout), ['coffee', extraArgsScript, 'arg']
ok stderr.match /^The script to be run begins with a shebang line with more than one/m
@@ -77,14 +79,14 @@ test "warn about non-portable shebang lines", ->
arrayEq JSON.parse(args), ['coffee', '--']
ok result.status is 0
result = spawnSync 'coffee', [initialSpaceScript, 'arg'], spawnOptions
result = spawnSync coffeeCommand, [initialSpaceScript, 'arg'], spawnOptions
stderr = result.stderr.toString()
ok stderr is ''
arrayEq JSON.parse(result.stdout), ['coffee', initialSpaceScript, 'arg']
ok result.status is 0
result = spawnSync(
'coffee', [initialSpaceExtraArgsScript, 'arg'], spawnOptions)
coffeeCommand, [initialSpaceExtraArgsScript, 'arg'], spawnOptions)
stderr = result.stderr.toString()
arrayEq JSON.parse(result.stdout), ['coffee', initialSpaceExtraArgsScript, 'arg']
ok stderr.match /^The script to be run begins with a shebang line with more than one/m
@@ -97,7 +99,7 @@ test "warn about non-portable shebang lines", ->
test "both warnings will be shown at once", ->
result = spawnSync(
'coffee', [initialSpaceExtraArgsScript, '--', 'arg'], spawnOptions)
coffeeCommand, [initialSpaceExtraArgsScript, '--', 'arg'], spawnOptions)
stderr = result.stderr.toString()
arrayEq JSON.parse(result.stdout), ['coffee', initialSpaceExtraArgsScript, 'arg']
ok stderr.match /^The script to be run begins with a shebang line with more than one/m

View File

@@ -329,7 +329,7 @@ test "export default implicit object", ->
test "export default multiline implicit object", ->
eqJS """
export default
foo: 'bar',
foo: 'bar'
baz: 'qux'
""",
"""
@@ -338,6 +338,22 @@ test "export default multiline implicit object", ->
baz: 'qux'
};"""
test "export default multiline implicit object with internal braces", ->
eqJS """
export default
foo: yes
bar: {
baz
}
quz: no
""",
"""
export default {
foo: true,
bar: {baz},
quz: false
};"""
test "export default assignment expression", ->
eqJS "export default foo = 'bar'",
"""
@@ -837,3 +853,70 @@ test "#4491: import- and export-specific lexing should stop after import/export
from('foo');
"""
# Issue #4874: Backslash not supported in import or export statements
test "#4874: backslash `import`", ->
eqJS """
import foo \
from 'lib'
foo a
""",
"""
import foo from 'lib';
foo(a);
"""
eqJS """
import \
foo \
from \
'lib'
foo a
""",
"""
import foo from 'lib';
foo(a);
"""
eqJS """
import \
utilityBelt \
, {
each
} from \
'underscore'
""",
"""
import utilityBelt, {
each
} from 'underscore';
"""
test "#4874: backslash `export`", ->
eqJS """
export \
* \
from \
'underscore'
""",
"""
export * from 'underscore';
"""
eqJS """
export \
{ max, min } \
from \
'underscore'
""",
"""
export {
max,
min
} from 'underscore';
"""

View File

@@ -0,0 +1,420 @@
test "#4798 destructuring of objects with splat within arrays", ->
arr = [1, {a:1, b:2}]
[...,{a, r...}] = arr
eq a, 1
deepEqual r, {b:2}
[b, {q...}] = arr
eq b, 1
deepEqual q, arr[1]
eq q.b, r.b
eq q.a, a
arr2 = [arr[1]]
[{a2...}] = arr2
eq a2.a, arr2[0].a
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, {}
# Should not trigger implicit call, e.g. rest ... => rest(...)
{
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
o = {
props: {
p: {
n: 1
m: 5
}
s: 6
}
}
{p: {m, q..., t = {obj...}}, r...} = o.props
eq m, o.props.p.m
deepEqual r, s: 6
deepEqual q, n: 1
deepEqual t, obj
@props = o.props
{p: {m}, r...} = @props
eq m, @props.p.m
deepEqual r, s: 6
{p: {m}, r...} = {o.props..., p:{m:9}}
eq m, 9
# Should not trigger implicit call, e.g. rest ... => rest(...)
{
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 }
# Should not trigger implicit call, e.g. rest ... => rest(...)
{ x, y, z ... } = { x: 1, y: 2, a: 3, b: 4 }
eq x, 1
eq y, 2
deepEqual z, { a: 3, b: 4 }
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
{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'
# Should not trigger implicit call, e.g. rest ... => rest(...)
{
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
(({
a
b
r ...
}) ->
eq 1, a
deepEqual r, {c: 3, d: 44, e: 55}
) {
obj2 ...
d: 44
e: 55
}
# Should not trigger implicit call, e.g. rest ... => rest(...)
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
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}}
# Should not trigger implicit call, e.g. rest ... => rest(...)
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}}
obj =
a:
b:
c:
d:
e: {}
obj9 = {a:1, obj.a.b.c..., g:3}
deepEqual obj9.d, {e: {}}
a = "a"
c = "c"
obj9 = {a:1, obj[a].b[c]..., g:3}
deepEqual obj9.d, {e: {}}
obj9 = {a:1, obj.a["b"].c["d"]..., g:3}
deepEqual obj9["e"], {}
test "#4673: complex destructured object spread variables", ->
b = c: 1
{{a...}...} = b
eq a.c, 1
d = {}
{d.e...} = f: 1
eq d.e.f, 1
{{g}...} = g: 1
eq g, 1
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
# Should not trigger implicit call, e.g. rest ... => rest(...)
(({
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}
f = ({ a: aa = 0, b: bb = 0 }) -> [aa, bb]
deepEqual [0, 0], f {}
deepEqual [0, 42], f {b:42}
deepEqual [42, 0], f {a:42}
deepEqual [42, 43], f {a:42, b:43}
test "#4673: complex destructured object spread variables", ->
f = ({{a...}...}) ->
a
eq f(c: 1).c, 1
g = ({@y...}) ->
eq @y.b, 1
g b: 1

View File

@@ -453,8 +453,7 @@ test 'inline implicit object literals within multiline implicit object literals'
eq 0, x.b
eq 0, x.a.aa
test "object keys with interpolations", ->
# Simple cases.
test "object keys with interpolations: simple cases", ->
a = 'a'
obj = "#{a}": yes
eq obj.a, yes
@@ -465,7 +464,7 @@ test "object keys with interpolations", ->
obj = {"#{5}"}
eq obj[5], '5' # Note that the value is a string, just like the key.
# Commas in implicit object.
test "object keys with interpolations: commas in implicit object", ->
obj = "#{'a'}": 1, b: 2
deepEqual obj, {a: 1, b: 2}
obj = a: 1, "#{'b'}": 2
@@ -473,7 +472,7 @@ test "object keys with interpolations", ->
obj = "#{'a'}": 1, "#{'b'}": 2
deepEqual obj, {a: 1, b: 2}
# Commas in explicit object.
test "object keys with interpolations: commas in explicit object", ->
obj = {"#{'a'}": 1, b: 2}
deepEqual obj, {a: 1, b: 2}
obj = {a: 1, "#{'b'}": 2}
@@ -481,7 +480,7 @@ test "object keys with interpolations", ->
obj = {"#{'a'}": 1, "#{'b'}": 2}
deepEqual obj, {a: 1, b: 2}
# Commas after key with interpolation.
test "object keys with interpolations: commas after key with interpolation", ->
obj = {"#{'a'}": yes,}
eq obj.a, yes
obj = {
@@ -504,17 +503,17 @@ test "object keys with interpolations", ->
"#{'c'}": 3, "#{'d'}": 4,
deepEqual obj, {a: 1, b: 2, c: 3, d: 4}
# Key with interpolation mixed with `@prop`.
test "object keys with interpolations: key with interpolation mixed with `@prop`", ->
deepEqual (-> {@a, "#{'b'}": 2}).call(a: 1), {a: 1, b: 2}
# Evaluate only once.
test "object keys with interpolations: evaluate only once", ->
count = 0
b = -> count++; 'b'
obj = {"#{b()}"}
eq obj.b, 'b'
a = -> count++; 'a'
obj = {"#{a()}"}
eq obj.a, 'a'
eq count, 1
# Evaluation order.
test "object keys with interpolations: evaluation order", ->
arr = []
obj =
a: arr.push 1
@@ -527,13 +526,13 @@ test "object keys with interpolations", ->
arrayEq arr, [1..7]
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7}
# Object starting with dynamic key.
test "object keys with interpolations: object starting with dynamic key", ->
obj =
"#{'a'}": 1
b: 2
deepEqual obj, {a: 1, b: 2}
# Comments in implicit object.
test "object keys with interpolations: comments in implicit object", ->
obj =
### leading comment ###
"#{'a'}": 1
@@ -564,7 +563,7 @@ test "object keys with interpolations", ->
}
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5}
# A more complicated case.
test "object keys with interpolations: more complicated case", ->
obj = {
"#{'interpolated'}":
"""
@@ -582,6 +581,212 @@ test "#4324: Shorthand after interpolated key", ->
eq 1, obj[1]
eq 2, obj.a
test "computed property keys: simple cases", ->
a = 'a'
obj = [a]: yes
eq obj.a, yes
obj = {[a]: yes}
eq obj.a, yes
obj = {[a]}
eq obj.a, 'a'
obj = {[5]}
eq obj[5], 5
obj = {['5']}
eq obj['5'], '5'
test "computed property keys: commas in implicit object", ->
obj = ['a']: 1, b: 2
deepEqual obj, {a: 1, b: 2}
obj = a: 1, ['b']: 2
deepEqual obj, {a: 1, b: 2}
obj = ['a']: 1, ['b']: 2
deepEqual obj, {a: 1, b: 2}
test "computed property keys: commas in explicit object", ->
obj = {['a']: 1, b: 2}
deepEqual obj, {a: 1, b: 2}
obj = {a: 1, ['b']: 2}
deepEqual obj, {a: 1, b: 2}
obj = {['a']: 1, ['b']: 2}
deepEqual obj, {a: 1, b: 2}
test "computed property keys: commas after key with interpolation", ->
obj = {['a']: yes,}
eq obj.a, yes
obj = {
['a']: 1,
['b']: 2,
### herecomment ###
['c']: 3,
}
deepEqual obj, {a: 1, b: 2, c: 3}
obj =
['a']: 1,
['b']: 2,
### herecomment ###
['c']: 3,
deepEqual obj, {a: 1, b: 2, c: 3}
obj =
['a']: 1,
['b']: 2,
### herecomment ###
['c']: 3, ['d']: 4,
deepEqual obj, {a: 1, b: 2, c: 3, d: 4}
test "computed property keys: key with interpolation mixed with `@prop`", ->
deepEqual (-> {@a, ['b']: 2}).call(a: 1), {a: 1, b: 2}
test "computed property keys: evaluate only once", ->
count = 0
a = -> count++; 'a'
obj = {[a()]}
eq obj.a, 'a'
eq count, 1
test "computed property keys: evaluation order", ->
arr = []
obj =
a: arr.push 1
b: arr.push 2
['c']: arr.push 3
['d']: arr.push 4
e: arr.push 5
['f']: arr.push 6
g: arr.push 7
arrayEq arr, [1..7]
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7}
test "computed property keys: object starting with dynamic key", ->
obj =
['a']: 1
b: 2
deepEqual obj, {a: 1, b: 2}
test "computed property keys: comments in implicit object", ->
obj =
### leading comment ###
['a']: 1
### middle ###
['b']: 2
# regular comment
'c': 3
### foo ###
d: 4
['e']: 5
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5}
obj = {
### leading comment ###
['a']: 1
### middle ###
['b']: 2
# regular comment
'c': 3
### foo ###
d: 4
['e']: 5
}
deepEqual obj, {a: 1, b: 2, c: 3, d: 4, e: 5}
test "computed property keys: more complicated case", ->
obj = {
['interpolated']:
['nested']:
123: 456
}
deepEqual obj,
interpolated:
nested:
123: 456
test "computed property keys: empty array as key", ->
o1 = { [[]] }
deepEqual o1, { [[]]: [] }
arrayEq o1[[]], []
o2 = { [[]]: 1 }
deepEqual o2, { [[]]: 1 }
eq o2[[]], 1
o3 = [[]]: 1
deepEqual o3, { [[]]: 1 }
deepEqual o3, { [[]]: 1 }
eq o3[[]], 1
o4 = a: 1, [[]]: 2
deepEqual o4, { a: 1, [[]]: 2 }
eq o4.a, 1,
eq o4[[]], 2
o5 = { a: 1, [[]]: 2 }
deepEqual o5, { a: 1, [[]]: 2 }
eq o5.a, 1,
eq o5[[]], 2
test "computed property keys: shorthand after computed property key", ->
a = 2
obj = {[1]: 1, a}
eq 1, obj[1]
eq 2, obj.a
test "computed property keys: shorthand computed property key", ->
a = 'b'
o = {[a]}
p = {a}
r = {['a']}
eq o.b, 'b'
eq p.a, o.b
eq r.a, 'a'
foo = -> "a"
obj = { [foo()] }
eq obj.a, 'a'
test "computed property keys: arrays", ->
b = 'b'
f = (c) -> "#{c}1"
obj =
['a']: [1, 2, 3]
[b]: [4, 5, 6]
[f(b)]: [7, 8, 9]
arrayEq obj.a, [1, 2, 3]
arrayEq obj.b, [4, 5, 6]
arrayEq obj.b1, [7, 8, 9]
test "computed property keys: examples from developer.mozilla.org (Object initializer)", ->
i = 0
obj =
['foo' + ++i]: i
['foo' + ++i]: i
['foo' + ++i]: i
eq obj.foo1, 1
eq obj.foo2, 2
eq obj.foo3, 3
param = 'size'
config =
[param]: 12,
['mobile' + param.charAt(0).toUpperCase() + param.slice(1)]: 4
deepEqual config, {size: 12, mobileSize: 4}
test "computed property keys: [Symbol.iterator]", ->
obj =
[Symbol.iterator]: ->
yield "hello"
yield "world"
arrayEq [obj...], ['hello', 'world']
test "computed property keys: Class property", ->
increment_method = "increment"
decrement_method = "decrement"
class Obs
constructor: (@count) ->
[increment_method]: -> @count += 1
[decrement_method]: -> @count -= 1
ob = new Obs 2
eq ob.increment(), 3
eq ob.decrement(), 2
test "#1263: Braceless object return", ->
fn = ->
return
@@ -652,20 +857,22 @@ test "#4544: Postfix conditionals in first line of implicit object literals", ->
test "#4579: Postfix for/while/until in first line of implicit object literals", ->
two =
foo:
bar: x for x in [1, 2, 3]
bar1: x for x in [1, 2, 3]
bar2: x + y for x, y in [1, 2, 3]
baz: 1337
arrayEq [1, 2, 3], two.foo.bar
arrayEq [1, 2, 3], two.foo.bar1
arrayEq [1, 3, 5], two.foo.bar2
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
bar1: x + y for x, y of a: 'b', c: 'd'
bar2: x + 'c' for x of a: 1, b: 2
baz: 1337
arrayEq ['ac', 'bc'], three.foo.bar
arrayEq ['ab', 'cd'], three.foo.bar1
arrayEq ['ac', 'bc'], three.foo.bar2
eq 1337, three.foo.baz
four =
@@ -696,4 +903,3 @@ test "#4579: Postfix for/while/until in first line of implicit object literals",
baz: 1337
arrayEq [4, 3, 2, 1, 0], six.foo.bar
eq 1337, six.foo.baz

View File

@@ -305,24 +305,6 @@ test "#2508: Existential access of the prototype", ->
eq NonExistent?::nothing, undefined
ok Object?::toString
test "power operator", ->
eq 27, 3 ** 3
test "power operator has higher precedence than other maths operators", ->
eq 55, 1 + 3 ** 3 * 2
eq -4, -2 ** 2
eq false, !2 ** 2
eq 0, (!2) ** 2
eq -2, ~1 ** 5
test "power operator is right associative", ->
eq 2, 2 ** 1 ** 3
test "power operator compound assignment", ->
a = 2
a **= 3
eq 8, a
test "floor division operator", ->
eq 2, 7 // 3
eq -3, -7 // 3

View File

@@ -116,3 +116,129 @@ test "#1012 slices with arguments object", ->
test "#1409: creating large ranges outside of a function body", ->
CoffeeScript.eval '[0..100]'
test "#2047: Infinite loop possible when `for` loop with `range` uses variables", ->
up = 1
down = -1
a = 1
b = 5
testRange = (arg) ->
[from, to, step, expectedResult] = arg
r = (x for x in [from..to] by step)
arrayEq r, expectedResult
testData = [
[1, 5, 1, [1..5]]
[1, 5, -1, []]
[1, 5, up, [1..5]]
[1, 5, down, []]
[a, 5, 1, [1..5]]
[a, 5, -1, []]
[a, 5, up, [1..5]]
[a, 5, down, []]
[1, b, 1, [1..5]]
[1, b, -1, []]
[1, b, up, [1..5]]
[1, b, down, []]
[a, b, 1, [1..5]]
[a, b, -1, []]
[a, b, up, [1..5]]
[a, b, down, []]
[5, 1, 1, []]
[5, 1, -1, [5..1]]
[5, 1, up, []]
[5, 1, down, [5..1]]
[5, a, 1, []]
[5, a, -1, [5..1]]
[5, a, up, []]
[5, a, down, [5..1]]
[b, 1, 1, []]
[b, 1, -1, [5..1]]
[b, 1, up, []]
[b, 1, down, [5..1]]
[b, a, 1, []]
[b, a, -1, [5..1]]
[b, a, up, []]
[b, a, down, [5..1]]
]
testRange d for d in testData
test "#2047: from, to and step as variables", ->
up = 1
down = -1
a = 1
b = 5
r = (x for x in [a..b] by up)
arrayEq r, [1..5]
r = (x for x in [a..b] by down)
arrayEq r, []
r = (x for x in [b..a] by up)
arrayEq r, []
r = (x for x in [b..a] by down)
arrayEq r, [5..1]
a = 1
b = -1
step = 0
r = (x for x in [b..a] by step)
arrayEq r, []
test "#4884: Range not declaring var for the 'i'", ->
'use strict'
[0..21].forEach (idx) ->
idx + 1
eq global.i, undefined
test "#4889: `for` loop unexpected behavior", ->
n = 1
result = []
for i in [0..n]
result.push i
for j in [(i+1)..n]
result.push j
arrayEq result, [0,1,1,2,1]
test "#4889: `for` loop unexpected behavior with `by 1` on second loop", ->
n = 1
result = []
for i in [0..n]
result.push i
for j in [(i+1)..n] by 1
result.push j
arrayEq result, [0,1,1]
test "countdown example from docs", ->
countdown = (num for num in [10..1])
arrayEq countdown, [10,9,8,7,6,5,4,3,2,1]
test "counting up when the range goes down returns an empty array", ->
countdown = (num for num in [10..1] by 1)
arrayEq countdown, []
test "counting down when the range goes up returns an empty array", ->
countup = (num for num in [1..10] by -1)
arrayEq countup, []
test "counting down by too much returns just the first value", ->
countdown = (num for num in [10..1] by -100)
arrayEq countdown, [10]
test "counting up by too much returns just the first value", ->
countup = (num for num in [1..10] by 100)
arrayEq countup, [1]

View File

@@ -313,3 +313,16 @@ test "#4248: Unicode code point escapes", ->
"""
/a\\udab3\\uddef/;
"""
test "#4811, heregex comments with ///", ->
eqJS """
///
a | # comment with ///
b # /// 'heregex' in comment will be consumed
///
""",
"""
// comment with ///
// /// 'heregex' in comment will be consumed
/a|b/;
"""

7
test/regex_dotall.coffee Normal file
View File

@@ -0,0 +1,7 @@
# Regex “dotall” flag, or `s`, is only supported in Node 9+, so put tests for
# the feature in their own file. The feature detection in `Cakefile` that
# causes this test to load is adapted from
# https://github.com/tc39/proposal-regexp-dotall-flag#proposed-solution.
test "dotall flag", ->
doesNotThrow -> /a.b/s.test 'a\nb'

View File

@@ -77,6 +77,14 @@ testRepl "empty command evaluates to undefined", (input, output) ->
input.emitLine ''
eq 'undefined', output.lastWrite()
testRepl "#4763: comment evaluates to undefined", (input, output) ->
input.emitLine '# comment'
eq 'undefined', output.lastWrite()
testRepl "#4763: multiple comments evaluate to undefined", (input, output) ->
input.emitLine '### a ### ### b ### # c'
eq 'undefined', output.lastWrite()
testRepl "ctrl-v toggles multiline prompt", (input, output) ->
input.emit 'keypress', null, ctrlV
eq '------> ', output.lastWrite(0)
@@ -116,7 +124,7 @@ testRepl "keeps running after runtime error", (input, output) ->
eq 'undefined', output.lastWrite()
testRepl "#4604: wraps an async function", (input, output) ->
return unless global.supportsAsync
return unless try new Function 'async () => {}' # Feature detect support for async functions.
input.emitLine 'await new Promise (resolve) -> setTimeout (-> resolve 33), 10'
setTimeout ->
eq '33', output.lastWrite()

View File

@@ -165,3 +165,9 @@ test "#2953: methods on endpoints in assignment from array splice literal", ->
delete Number.prototype.same
arrayEq [0, 5, 9], list
test "#1726: `Op` expression in property access causes unexpected results", ->
a = [0..2]
arrayEq a, a[(!1 in a)..]
arrayEq a, a[!1 in a..]
arrayEq a[(!1 in a)..], a[(!1 in a)..]