Compare commits

..

233 Commits
1.6.3 ... 1.7.0

Author SHA1 Message Date
Jeremy Ashkenas
a3ae0c5c75 CoffeeScript 1.7.0 2014-01-28 14:05:38 -05:00
Jeremy Ashkenas
46895419b3 Merge pull request #3333 from xixixao/release1.7.0
Implement #3332
2014-01-28 10:59:22 -08:00
xixixao
4f6a4bab30 Removed idle styles 2014-01-28 18:57:37 +00:00
xixixao
9e5ffaea69 Use actual _.template instead of custom one 2014-01-28 18:56:43 +00:00
xixixao
928d2d7f4a Extras compiled with 1.7.0 2014-01-28 03:35:09 +00:00
xixixao
10293df1f9 Recompile source with 1.7.0 version 2014-01-28 03:34:00 +00:00
xixixao
41c4c822ba Recompile documentation js files with 1.7.0 version 2014-01-28 03:33:03 +00:00
xixixao
6030ac3a23 Fixes in documentation 2014-01-28 03:30:06 +00:00
xixixao
f42329ca0a Use Node instead of Ruby for documentation 2014-01-28 03:17:12 +00:00
Jeremy Ashkenas
570529f526 Fixing tests for browser. 2014-01-27 11:55:20 -05:00
Jeremy Ashkenas
8b8436d794 Merge pull request #3326 from xixixao/release1.7.0
Prepare 1.7.0 release
2014-01-27 08:42:58 -08:00
Jeremy Ashkenas
b0594aad3e Merge pull request #3331 from xixixao/inlineobjectchain
Fixes chaining after inline implicit objects
2014-01-26 14:15:09 -08:00
xixixao
04b0b94a8c Fixes chaining after inline implicit objects 2014-01-26 22:11:10 +00:00
xixixao
e1f46cfb9b Prepare 1.7.0 release 2014-01-26 18:32:12 +00:00
Michael Ficarra
cc1b74f11b Merge pull request #3329 from xixixao/issue3325
Fixes #3325: implicit indendation error messages
2014-01-25 22:00:24 -08:00
xixixao
104b4666fe Fix indendation error messages 2014-01-26 05:25:13 +00:00
Michael Ficarra
835fc84330 Merge pull request #3328 from xixixao/issue3216
Fixes #3216 for declarations in object literals
2014-01-25 21:05:34 -08:00
Michael Ficarra
a5d6285cfd forgotten compilation from parent commit 2014-01-25 22:54:25 -06:00
xixixao
c2727d964c Fix for declarations in object literals 2014-01-26 04:41:30 +00:00
Michael Ficarra
d687d52f9e Merge pull request #2887 from epidemian/more-math-operators
Add new mathematical operators
2014-01-24 13:18:29 -08:00
Demian Ferreiro
6a43de789f Simplify modulo tests 2014-01-24 17:55:50 -03:00
Demian Ferreiro
1288786fdc Make modulo operator convert arguments to numbers 2014-01-24 16:08:39 -03:00
Demian Ferreiro
2b4421fca1 Merge branch 'master' into more-math-operators
Conflicts:
	lib/coffee-script/grammar.js
	lib/coffee-script/lexer.js
	lib/coffee-script/nodes.js
	lib/coffee-script/parser.js
	test/regexps.coffee
2014-01-24 15:40:28 -03:00
Jeremy Ashkenas
6bdd52733e Merge pull request #3327 from xixixao/forexpansion
Fix expansion in destructuring inside comprehensions
2014-01-24 10:24:51 -08:00
xixixao
bd6b4142fe Fix expansion in destructuring inside comprehensions 2014-01-24 18:20:45 +00:00
Jeremy Ashkenas
daa6ad5470 Merge pull request #3268 from xixixao/issue156
Implement #156, Added expansion to destructuring
2014-01-24 08:33:42 -08:00
xixixao
369e0545c0 Added expansion to destructuring 2014-01-24 16:00:34 +00:00
Michael Ficarra
5f31a3d60e Merge pull request #3324 from xixixao/issue1871
Fixes #1871, close implicit objects in implicit returns
2014-01-23 15:24:10 -08:00
xixixao
8b976acac1 Fixes #1871, close implicit objects in implicit returns 2014-01-23 23:12:12 +00:00
Jeremy Ashkenas
b00962db1a Merge pull request #3322 from xixixao/issue1099
Fix #1099, remove in empty array optimization
2014-01-23 07:16:00 -08:00
xixixao
26dcf025f4 Remove in empty array optimization 2014-01-23 15:09:25 +00:00
Jeremy Ashkenas
d2f90d2236 Merge pull request #3320 from xixixao/issue1275
Fix #1275
2014-01-22 10:54:18 -08:00
xixixao
c3391e1dd8 Fix 1275 2014-01-22 18:33:44 +00:00
Jeremy Ashkenas
b3463a1378 Merge pull request #3319 from xixixao/issue1096
Fix 1096, 1131, 1828: Improve error messages for generated tokens
2014-01-22 05:29:03 -08:00
xixixao
f0463e9981 Improve error messages for generated tokens 2014-01-22 02:54:09 +00:00
Jeremy Ashkenas
1cc583b382 Merge pull request #3317 from xixixao/chainafteroutdent
Chaining semantics after arguments with outdent
2014-01-21 14:27:32 -08:00
xixixao
39cb8815f7 Fixed chaining semantics after outdent 2014-01-21 22:00:57 +00:00
Michael Ficarra
21db08a23d add missing implicit object literal test
ref michaelficarra/CoffeeScriptRedux#266 and 670a1f5f78 (diff-2)
2014-01-12 11:16:27 -06:00
Michael Ficarra
1f301d8c07 Merge pull request #3292 from marchaefner/CLI
`coffee DIR` executes `DIR/index.coffee`
2013-12-29 14:24:02 -08:00
Jeremy Ashkenas
35f185440f Merge pull request #3293 from marchaefner/master
Fix `child_process.fork` patch
2013-12-23 22:19:55 -08:00
Michael Ficarra
5ce0c84907 Merge pull request #3284 from xixixao/issue3056
Fix multiple postfix conditionals
2013-12-23 22:08:34 -08:00
Marc Häfner
a6891e4feb Fix child_process.fork patch
* Preserve `options` when called without `args`
  * Don't use `coffee` script as `execPath` (Fixes #2919)
2013-12-24 02:05:53 +01:00
Marc Häfner
8a3ebb9181 CLI: Run index.coffee when called on a directory 2013-12-24 01:53:27 +01:00
Michael Ficarra
308299fe04 Merge pull request #3285 from xixixao/formatutils
Format utilities using single quoted literals
2013-12-16 20:08:05 -08:00
xixixao
1cc8463b9c Formatted utilities using single quoted literals 2013-12-17 03:36:49 +01:00
xixixao
d7862647d9 Fix multiple postfix conditionals 2013-12-17 03:31:19 +01:00
Nami-Doc
6c786f0fb7 Merge pull request #3283 from Fritz-Lium/patch-1
the page lists only top 100 contributors
2013-12-14 03:31:43 -08:00
Fritz-Lium
cc345def46 the page list only top 100 contributors 2013-12-14 19:31:04 +08:00
Michael Ficarra
818983b6a4 Merge pull request #3280 from xixixao/issue3271
Examples: Back to non-naked constructor, @, preincrement, comprehension bracketing...
2013-12-09 14:31:23 -08:00
xixixao
b859d92d2c Back to non-naked constructor, @, preincrement, comprehension bracketing, idioms: calls, comprehensions, interpolations 2013-12-09 22:28:34 +00:00
Jeremy Ashkenas
8cd9ba168d Merge pull request #3279 from michaelficarra/fix-require.extensions-registration
fix require.extensions registration
2013-12-08 13:26:15 -08:00
Michael Ficarra
08a57898a7 add CoffeeScript.register method for require.extensions registration 2013-12-08 14:21:18 -06:00
Michael Ficarra
ba4743cc83 fix auto and manual require.extensions registration; ref #3141
You can now `require('coffee-script/register')` to manually register,
and the compiler auto-registers when directly running a coffee file.
2013-12-08 14:19:10 -06:00
Jeremy Ashkenas
94e22ab819 Merge pull request #3275 from marchaefner/CLI
Fix --watch handling of deleted sources
2013-12-07 22:28:19 -08:00
Michael Ficarra
c1b46f777f Merge pull request #3277 from boundvariable/master
Add CoffeeScript in Action book link
2013-12-06 08:04:46 -08:00
Patrick Lee
81cf9ca00f Add CoffeeScript in Action book link 2013-12-07 00:58:43 +11:00
Nami-Doc
96f087ca6b Merge pull request #3276 from xixixao/issue1495fixup
Fixup #3263: Prevent loop collection in endAllImplicitCalls
2013-12-05 13:55:38 -08:00
xixixao
bc975e556e Prevent loop collection in endAllImplicitCalls 2013-12-05 21:53:42 +00:00
Marc Häfner
6804c1065b Fix: EPERM when deleting watched dirs in Windows
* Suppress `EPERM` from watchers
  * Suppress `EPERM` from `fs.unlink`
2013-12-04 17:12:24 +01:00
Marc Häfner
74cf54a84f Prettify watch 2013-12-04 14:50:36 +01:00
Marc Häfner
73af30b5d8 Fixes #3267 -- Remove source maps of deleted source files 2013-12-01 12:03:22 +01:00
Marc Häfner
54633aee3f Remove unnecessary parameter 2013-12-01 11:35:03 +01:00
Marc Häfner
26200f4640 Improve HEREDOC regexp
* Exclude trailing blank line from the match group
  * Fix backslash handling
2013-11-28 16:46:00 +01:00
Marc Häfner
13f205404c Merge pull request #3261 from xixixao/issue1273
Fix #1273, Handle backslashes at the end of heredocs
2013-11-28 07:32:20 -08:00
Jeremy Ashkenas
563f14b178 Merge pull request #3263 from xixixao/issue1495
Implement #1495,  Method call chaining
2013-11-27 22:58:46 -08:00
xixixao
5e4cca90a3 Fix #3264, missing leading whitespace before interpolation in heredoc 2013-11-27 20:53:20 +00:00
xixixao
a61b6ee925 Fixed leading whitespace before interpolation in simple strings 2013-11-27 20:29:45 +00:00
xixixao
b11d956d53 Added compilation regression test 2013-11-27 12:58:14 +00:00
xixixao
ee9febe399 Handle nested calls and function oneliners when chaining 2013-11-27 04:57:44 +00:00
xixixao
15a70f863c Implemented method call chaining 2013-11-27 03:41:52 +00:00
Michael Ficarra
4eee9c318e Merge pull request #3262 from xixixao/issue1766
Fix #1766, Add negative slice end index into docs
2013-11-26 19:10:46 -08:00
xixixao
ac6a76ee10 Add negative slice end index into docs 2013-11-27 01:01:32 +00:00
xixixao
42aa8d256c Handle backslashes at the end of heredocs 2013-11-26 19:29:13 +00:00
Marc Häfner
873ed071d4 Fixes #3259 -- Use placeholders when adding params to scope
Don't register nested variables of complex parameters as parameters of the compiled function. Use the computed placeholder name instead.
2013-11-26 14:23:20 +01:00
Jeremy Ashkenas
210376f7a9 Merge pull request #3256 from xixixao/issue3249
Implements #3249, #1994 Escape newlines in heredocs with backslashes
2013-11-24 10:54:53 -08:00
xixixao
35d327a304 Escape newlines in heredocs with backslashes 2013-11-24 18:37:11 +00:00
Jeremy Ashkenas
e3e8b1c501 Merge pull request #3255 from marchaefner/CLI
CLI fixes and refactoring
2013-11-23 18:31:27 -08:00
Marc Häfner
52a54a7681 Fix and simplify management of sources in CLI
* Move all source path filtering to `compilePath`
  * Restrict modification of `sources` to
    * `compilePath` for adding
    * `removeSource` for removing
  * Don't add unfiltered paths to `sources` (and remove them later on)
2013-11-23 07:54:35 +01:00
Marc Häfner
1fe28c1fc9 Simplify and rename unwatchDir (to removeSourceDir) 2013-11-23 06:55:57 +01:00
Marc Häfner
89efd05a3f Make timeout callback in watchDir synchronous 2013-11-23 06:49:41 +01:00
Marc Häfner
caafafcf4d Simplify removeSource and make it synchronous 2013-11-23 06:49:13 +01:00
Marc Häfner
81d8224b9a Remove path.exists* as fallback for fs.exists* 2013-11-23 06:42:39 +01:00
Marc Häfner
22c85e216f Use absolute paths in CLI
* Use absolute paths for source files and target paths
  * Memorize and explicitly check for watched directories
  * Make path comparison and construction more precise and clear
  * Remove now unnecessary check for special relative paths
2013-11-23 06:41:08 +01:00
Michael Ficarra
f047ba52b2 Merge pull request #3250 from xixixao/issue3229
Fix multiple escaped backslashes in literal strings
2013-11-19 20:32:41 -08:00
xixixao
130899a39f Fix multiple escaped backslashes in literal strings 2013-11-19 23:44:39 +00:00
Jeremy Ashkenas
6847400ccb Merge pull request #3246 from xixixao/issue3229
Implements #3229 - Changed multiline string literals
2013-11-18 16:42:10 -08:00
xixixao
1102567b0c Handle escaped backslashes 2013-11-19 00:04:17 +00:00
xixixao
8c6647849b Don't rely on multiline in other tests 2013-11-18 16:26:48 +00:00
xixixao
de42ad0e1c More test cases 2013-11-18 15:25:11 +00:00
xixixao
073d025fac Better method name and fixed regexps for IE 2013-11-18 15:13:40 +00:00
xixixao
efe8c68c75 Changed multiline string literals 2013-11-18 04:32:15 +00:00
Jeremy Ashkenas
45d97b3dfe Remove old Node pre-0.6 warning. 2013-11-15 14:26:47 -05:00
Jeremy Ashkenas
fcc88ca472 Revert "add: Stat polling support while fs.watch doesn't work."
This reverts commit 2853e718f2.
2013-11-15 14:26:16 -05:00
Jeremy Ashkenas
187ebd0374 Revert "mov: Change the option name --polling to --watch-polling, and only leaves the long option name."
This reverts commit 08f6c65c3b.
2013-11-15 14:26:05 -05:00
Jeremy Ashkenas
c4999efda7 Revert "fix: opts.polling changed to opts[watch-polling]"
This reverts commit 52789f5b19.
2013-11-15 14:25:55 -05:00
Jeremy Ashkenas
b6231e50c3 Merge pull request #3212 from ysmood/issue3210
add: Stat polling support while `fs.watch` doesn't work.
2013-11-15 11:09:50 -08:00
Marc Häfner
592aa33577 Fixes #2367 -- super in for-loop 2013-11-15 06:35:04 +01:00
Marc Häfner
544c99a9ad Fix error location for illegal pure statements 2013-11-15 05:37:34 +01:00
Jeremy Ashkenas
de0e3baf1f Merge pull request #3237 from marchaefner/fixSuper
Fix super-related tagging of `Code` nodes
2013-11-14 19:25:50 -08:00
Marc Häfner
aea0f2533b Fixes #3232 -- Tag all class properties static
(and remove duplicate `context` assigment)
2013-11-15 03:44:26 +01:00
Marc Häfner
138c25fe5f Cleanup and extend METHOD_DEF
* Fixes #2949: Detect reserved names (not only for instance methods)
  * Don't assign names which might result in incorrect `super` calls
2013-11-12 16:53:09 +01:00
Jeremy Ashkenas
e0195756dc Merge pull request #3234 from marchaefner/issue3087
Fixes #3087 -- Use `fs.*Sync` for CLI compilation
2013-11-10 04:19:49 -08:00
Marc Häfner
96ae98fade Fixes #3087 -- Use fs.*Sync for CLI compilation
* Make `compilePath` synchronous
 * Remove unused variable
2013-11-10 08:36:29 +01:00
Nami-Doc
efb9809d3b Merge pull request #3233 from xixixao/issue2953cleanup
Clean up `Method calls on splice endpoints`
2013-11-09 16:20:13 -08:00
xixixao
0dada3dd27 Remove unnecessary type conversions to Number 2013-11-10 00:13:52 +00:00
xixixao
849c8e8ef4 Remove unnecessary existential check 2013-11-10 00:13:14 +00:00
Jeremy Ashkenas
9e9c83f788 Merge pull request #3228 from marchaefner/fixClassCompilation
Avoid unnecessary wrapping of some bound functions
2013-11-05 18:47:01 -08:00
Marc Häfner
d41d87a874 Avoid unnecessary wrapping of some bound functions 2013-11-03 03:14:13 +01:00
Jeremy Ashkenas
ab40571ffc Merge pull request #3227 from marchaefner/fixClassCompilation
Fix scope of external constructor reference / Refactor `Closure`
2013-11-01 13:08:27 -07:00
Marc Häfner
5d13d14de9 Closes #3008 -- Fix scope of constructor reference
* Make scope of `Code` nodes accessible (prior to `compileNode`)
* Use correct scope for reference of external constructor.
* Remove unreachable code.
2013-11-01 01:13:10 +01:00
Marc Häfner
1df8abf1cb Refactor closure compilation
* Break up `Closure` and merge `Closure.wrap` into `Base.compileClosure`
* Construct class closure directly in `Class.compileNode`
* Reuse `isLiteralArguments` in `Range.compileArray`
* Move all helpers to bottom of file
* Add test for #3063
2013-10-31 23:25:11 +01:00
Jeremy Ashkenas
45b60c9a52 Merge pull request #3224 from marchaefner/fixClassCompilation
Minor fixes for class compilation
2013-10-28 17:51:23 -07:00
Marc Häfner
091bc56a96 Code cleanup in Class
* Don't insert unnecessary `_ref` (in the wrong scope)
* Improve code prettiness. (Reverts most of 903e9c99)
2013-10-29 00:01:20 +01:00
Yad Smood
52789f5b19 fix: opts.polling changed to opts[watch-polling] 2013-10-27 01:04:03 +08:00
Marc Häfner
9ba1d41ec8 Fix: __extends helper above directive prologue 2013-10-26 06:54:54 +02:00
Jeremy Ashkenas
5456bd5f89 Merge pull request #3218 from marchaefner/issue3059
Escapable linebreaks in heregexes
2013-10-23 16:03:32 -07:00
Marc Häfner
0d662c3ad2 Missing parentheses 2013-10-24 00:43:29 +02:00
Marc Häfner
fa76e2dd21 Escapable linebreaks in heregexes 2013-10-24 00:36:46 +02:00
Yad Smood
08f6c65c3b mov: Change the option name --polling to --watch-polling, and only leaves the long option name.
opt: Optimize the comment of the `--watch-polling` option.
2013-10-23 00:52:40 +08:00
Jeremy Ashkenas
54840c0cbf Merge pull request #3214 from marchaefner/issue3059
Escaped whitespace and slashes in Heregexes
2013-10-22 09:35:00 -07:00
Marc Häfner
91ac3fa031 Escaped whitespace and slashes in Heregexes
* Resolves #3059: Don't remove escaped whitespace.
* Fixes #2238: Prevent escaping slashes that are already escaped.
* Fix detection of end of heregex with escaped slashes.
2013-10-22 18:08:17 +02:00
Yad Smood
2853e718f2 add: Stat polling support while fs.watch doesn't work.
Add a new cli option: -g --polling [SPAN]

    If state polling mode is enabled, use it.
    Else use the native api.

    This is useful while watching remote directory.
    Such as the `fs.watch` won't catch the SMB server's file change event.
2013-10-21 15:26:20 +08:00
Jeremy Ashkenas
c22707cd53 Fixes #2941 -- don't destroy extensionless filenames for --join 2013-10-20 19:09:55 -03:00
Jeremy Ashkenas
351c875576 merged in stricter noncallables 2013-10-20 18:49:30 -03:00
Jeremy Ashkenas
35b64d7f18 Merge pull request #3211 from marchaefner/issue2181
Fix compilation for conditional assignment
2013-10-20 14:16:38 -07:00
Jeremy Ashkenas
db87d817e8 Merge pull request #3012 from imcotton/parallel-loading
Script loading parallelized in browser
2013-10-20 14:10:40 -07:00
Jeremy Ashkenas
f3c5cc6774 Fixes #3019 - Documentation tweak to default argument meaning. 2013-10-20 18:08:28 -03:00
Marc Häfner
4cc2c305a4 Fixes #2181 -- conditional assignment as subexpression
* Parenthesize compilation of `||=` and `&&=` (when needed).
* Fix variable caching for `?=`
2013-10-20 22:59:01 +02:00
Jeremy Ashkenas
a5513c45d0 Fixes #3047 -- Fixes module.paths when running directly with no explicit passed-in files. 2013-10-20 17:50:13 -03:00
Jeremy Ashkenas
c820e0241e Fixes #3053 - error for mismatched own/for-in without an index. 2013-10-20 17:40:50 -03:00
Jeremy Ashkenas
eb2ac2c64d Fixes #3063 -- wait a moment so that an error can be raised. 2013-10-20 17:04:52 -03:00
Jeremy Ashkenas
59cf19fd1c Fixes #3072 -- tweak process.argv to match when running REPL 2013-10-20 16:53:08 -03:00
Jeremy Ashkenas
d5a25d138d Fixes #3089 -- don't mutate options passed in to compile() 2013-10-20 16:21:06 -03:00
Jeremy Ashkenas
a7ecd80c92 Merge pull request #3096 from marchaefner/issue2994
Disallow single-line `IF expr ELSE` without `THEN`
2013-10-20 12:13:39 -07:00
Jeremy Ashkenas
465cffc675 Merge pull request #3113 from mklement0/make-repl-use-global-context
Make the REPL *CLI* use the global context to be consistent with the node REPL *CLI*.
2013-10-20 09:17:07 -07:00
Jeremy Ashkenas
302a46d093 Merge pull request #3132 from caitp/issue-3132
Format block-comments better
2013-10-20 09:08:40 -07:00
Jeremy Ashkenas
392767a04e Fixes #3143 -- Potential memory leaks caused by use of fat arrow next to other (non-fat-arrow-using) long-lived closures. 2013-10-20 12:53:18 -03:00
Jeremy Ashkenas
928f949761 Fixes #3160 -- a missing bit of locationData 2013-10-20 12:15:15 -03:00
Jeremy Ashkenas
8bb833d858 Merge pull request #3165 from grschafer/master
Fix constructor_destructuring docs example to alert a defined value
2013-10-20 08:04:44 -07:00
Jeremy Ashkenas
2b03fa9077 Fixes #3166 -- add a (simpler) flag to suppress the generated header. 2013-10-20 12:03:37 -03:00
Jeremy Ashkenas
cfdb774da9 CoffeeScript REPL should be able to require coffeescript files. 2013-10-20 11:22:23 -03:00
Jeremy Ashkenas
b173a377a6 Fixes #3208. You now have to require 'coffee-script/extensions' in order to be able to auto-require CoffeeScript files. 2013-10-20 11:08:13 -03:00
Jeremy Ashkenas
581af4540a Merge pull request #3193 from celwell/master
fixed ascii art inconsistencies in coffee's water vapor
2013-10-04 04:04:10 -07:00
Christopher Elwell
18e5b6b199 fixed ascii art inconsistencies in coffee's water vapor 2013-10-03 19:02:28 -07:00
Michael Ficarra
6da2306fe2 Merge pull request #3189 from mal/issue3186
Fixes #3186
2013-09-29 07:42:36 -07:00
Mal Graty
a8e4b78803 Fixes #3186 2013-09-29 15:28:58 +01:00
Nami-Doc
a3be1f6e48 Merge pull request #3174 from a3gis/master
Accept all format of numbers in ranges
2013-09-24 11:20:57 -07:00
a3gis
89ef3d4117 accept all format of numbers in ranges 2013-09-24 19:15:31 +01:00
Jeremy Ashkenas
4cf75ec027 Forgot to update the .erb as well. 2013-09-24 09:52:00 -03:00
Jeremy Ashkenas
830c294aea Add new Packt book. 2013-09-22 14:56:33 -03:00
a3gis
bb86e54ece accept all format of numbers in ranges 2013-09-21 00:51:12 +01:00
Caitlin Potter
359e17277f Merge pull request #1 from sjorek/issue-3132
Enhancement: Add more block-comment related tests
2013-09-17 10:12:31 -07:00
Stephan Jorek
89f5f9d59d added more block-comment related tests for single-line block-comments and jsdoc-like @doctags-comments. 2013-09-17 18:09:15 +02:00
Greg Schafer
26c0f7ca2d Fix constructor_destructuring example to alert a defined value 2013-09-14 14:34:53 -05:00
Michael Ficarra
40c1086efa Merge pull request #3151 from kchmck/master
Fix some inconsistent indentation
2013-09-03 16:33:27 -07:00
Mick Koch
999a3db499 Fix some inconsistent indentation
Some places used 4 spaces instead of 2
2013-09-03 19:02:18 -04:00
Michael Klement
50e13f62f2 Merge branch 'make-repl-use-global-context' of https://github.com/mklement0/coffee-script into make-repl-use-global-context 2013-09-03 18:35:21 -04:00
Michael Klement
fceff1729c Make the REPL *CLI* use the global context so as to be consistent with the node REPL CLI.
Make the REPL *CLI* use the global context so as to (a) be consistent
with the `node` REPL CLI and, therefore, (b) make packages that modify
native prototypes (such as 'colors' and 'sugar') work as expected.

Note that, by contrast, programmatic use (`require 'repl'`) will
continue to default to a NON-global context - again, consistent with
node's behavior.
2013-09-03 18:19:43 -04:00
Michael Ficarra
1765a7ae0c Merge pull request #3150 from mklement0/fix-repl-module-global-context-support
Fix: support for consumers of the REPL *module* being able to opt into using the global context ...
2013-09-03 14:47:22 -07:00
Michael Klement
ae79ff9fa3 Merge branch 'make-repl-use-global-context' of https://github.com/mklement0/coffee-script into make-repl-use-global-context 2013-09-03 17:28:03 -04:00
Michael Klement
3e9d01d6c6 Make the REPL *CLI* use the global context so as to be consistent with the node REPL CLI.
Make the REPL *CLI* use the global context so as to (a) be consistent
with the `node` REPL CLI and, therefore, (b) make packages that modify
native prototypes (such as 'colors' and 'sugar') work as expected.

Note that, by contrast, programmatic use (`require 'repl'`) will
continue to default to a NON-global context - again, consistent with
node's behavior.
2013-09-03 17:27:13 -04:00
Michael Klement
ae4535d639 Fix: support for consumers of the REPL *module* being able to opt into using the global context via option .useGlobal.
Note that, at least for now, CoffeeScript's own REPL *CLI* still uses a
non-global context, rendering modules such as `color`, which attempt to
modify the prototypes of JavaScript primitives, ineffective. By
contrast, node's own CLI does use the global context.
2013-09-03 16:41:27 -04:00
a3gis
c5120c7980 fix exit code when using --nodejs option 2013-09-02 16:11:22 -05:00
Jeremy Ashkenas
92e83489fc Merge pull request #3146 from phillipalexander/fix-underscore-docs
Fix broken formatting in underscore.coffee docs
2013-09-02 13:33:26 -07:00
Caitlin Potter
1b7491d63d Fixes #3132 - Improve rendering of block-comments 2013-08-23 20:53:18 -04:00
Phillip Alexander
ce14ad764a Fix formatting issues in underscore.coffee documentation (generated html)
Use docco to regenerate documentation for underscore.coffee.
2013-08-20 09:57:52 -07:00
Jeremy Ashkenas
96e807c677 Improve license part of package.json ;) 2013-08-19 16:10:25 +02:00
Michael Klement
70994d4b50 Refactored inline-if into more readable multi-line statement. 2013-08-07 11:40:11 -04:00
Michael Klement
675095efbe Amended - Make the REPL *CLI* use the global context so as to be consistent with the node REPL CLI.
(My apologies: In the previous commit I accidentally made `useGlobal:
yes` the default for _programmatic_ use also, but the intent was to
only do it for the stand-alone *CLI*.)

Make the REPL *CLI* use the global context so as to (a) be consistent
with the `node` REPL CLI and, therefore, (b) make packages that modify
native prototypes (such as 'colors' and 'sugar') work as expected.

Note that, by contrast, programmatic use (`require 'repl'`) will
continue to default to a NON-global context - again, consistent with
node's behavior.
2013-08-07 08:59:27 -04:00
Michael Klement
0235d12927 Make the REPL use the global context to be consistent with the node REPL.
This will make packages that modify prototypes - e.g. 'colors', 'sugar'
- work as expected.

To verify that the `node` REPL uses the global context, execute `global
=== module.exports.repl.context`.

Note: Tests pass, except `cluster.coffee`, which, however, failed even
before these modifications.
2013-08-06 21:28:34 -04:00
Michael Ficarra
9d24a3420d Merge pull request #3111 from benbria/master
Issue #3092: Fix column numbers in sourcemaps to not be essentially random.
2013-08-06 13:52:52 -07:00
Jason Walton
3ad332d5d4 Issue #3092: Fix column numbers in sourcemaps to not be essentially random. 2013-08-06 16:25:23 -04:00
Michael Ficarra
15517df417 Merge pull request #3107 from mal/issue2957
Fork with binary of coffee-script in use, rather than global
2013-08-02 15:44:55 -07:00
Mal Graty
3c2f0d174e Use coffee binary of coffee that overrode fork
This solves two potential problems when it comes to forking:

    1) Forking will now work correctly even when `coffee` is not installed
       globally.
    2) Forking when using a locally installed version of `coffee` will fork
       using that version, and not fallback to a globally installed version.

Fixes #2957
2013-08-02 23:10:45 +01:00
Jeremy Ashkenas
8cf6f62ea4 Merge pull request #3100 from epidemian/issue3023
Fix #3023, Change how error messages are shown
2013-08-02 13:05:37 -07:00
Demian Ferreiro
9e716b310d Avoid using a getter for the compiler error's "stack" property
Instead, set the "stack" property manually when the error gets updated on re-throws.
2013-08-02 01:52:36 -03:00
Michael Ficarra
e44bf9ae81 Merge pull request #3104 from davidchambers/recompile
recompile
2013-08-01 14:17:14 -07:00
David Chambers
f5f99b3022 recompile 2013-08-01 14:14:12 -07:00
Jeremy Ashkenas
dc3d70e696 cleaning up mkdirp bit. 2013-08-01 11:12:41 -04:00
Jeremy Ashkenas
77fded3c5e Merge pull request #3101 from FredyC/master
Fixed deep directory creation for command line utility
2013-08-01 08:11:07 -07:00
FredyC
e644f7244d Using original existence check with mkdirp call on failure 2013-08-01 17:03:32 +02:00
FredyC
457cdfde26 Fixed deep directory creation for command line utility 2013-08-01 11:17:27 +02:00
Marc Häfner
910e38749c Merge removeMidExpressionNewlines into addImplicitIndentation
and rename it to `normalizeLines`
2013-07-31 22:12:44 +02:00
Demian Ferreiro
2b4a37296f Override the SyntaxError's "stack" property instead of deleting it
This makes the "stack" property more useful when it's shown on other Node.js applications that compile CoffeeScript (e.g. testing libraries) and should fix #3023. A minimal example:

    $ node -e 'require("coffee-script").compile("class class")'

    /usr/lib/node_modules/coffee-script/lib/coffee-script/coffee-script.js:41
            throw err;
                  ^
    [stdin]:1:7: error: unexpected CLASS
    class class
          ^^^^^
2013-07-31 09:24:43 -03:00
Demian Ferreiro
3f9cdcf1fa Change how error messages are shown
Instead of throwing the syntax errors with their source file location and needing to then catch them and call a `prettyErrorMessage` function in order to get the formatted error message, now syntax errors know how to pretty-print themselves (their `toString` method gets overridden).

An intermediate `catch` & re-`throw` is needed at the level of `CoffeeScript.compile` and friends. But the benefit of this approach is that now libraries that use the `CoffeeScript` object directly don't need to bother catching the possible compilation errors and calling a special function in order to get the nice error messages; they can just print the error itself (or let it bubble up) and the error will know how to pretty-print itself.
2013-07-31 08:27:49 -03:00
Marc Häfner
4342bedd2f Fix multi-line if-else in single-line expression.
* When searching for the closing token of a single-line expression, ignore TERMINATORS that will subsequently be removed.
* Add a test.
2013-07-31 12:18:50 +02:00
Marc Häfner
fdd5796f5e Disallow single-line IF expr ELSE without THEN
* Supplement missing block before `ELSE` token only for multi-line `if`.
* Don't prematurely remove `TERMINATOR` before `ELSE` (so we can  differentiate single- and multi-line forms).
* Cleanup: Remove `WHEN` from `EXPRESSION_CLOSE`. (Only `LEADING_WHEN` tokens are preceded by a `TERMINATOR`.)
* Cleanup: Remove really old, inapplicable text from comment.
2013-07-30 19:31:46 +02:00
Jeremy Ashkenas
f48aa44386 Merge pull request #3094 from epidemian/compile-js
Update compiled JS
2013-07-29 23:17:38 -07:00
Demian Ferreiro
51c625205b Update compiled JS 2013-07-30 01:06:41 -03:00
Michael Ficarra
e581f7d2f0 Merge pull request #3051 from CaseyLeask/source-map-syntax-update
Updated the Source Maps syntax
2013-07-04 07:03:00 -07:00
Cotton Hou
3aa646e425 rebuild for PR #3012 2013-06-28 09:40:22 +08:00
Cotton Hou
92208fec44 simplify logic and changing less from before 2013-06-28 09:28:20 +08:00
Cotton Hou
cdc603c794 remove logic redundancy 2013-06-28 09:28:20 +08:00
Cotton Hou
46d8902004 parallelized script loading in browser, yet order remain 2013-06-28 09:28:20 +08:00
Casey Leask
34c1704286 Removed multi-line comment wrapping 2013-06-26 13:11:13 +00:00
Casey Leask
19767a0f10 Updated the Source Maps syntax 2013-06-26 12:34:21 +00:00
Nami-Doc
0c9f0fd099 Merge pull request #3049 from dpatti/extensions-scope
Avoid variable scope collision with extensions
2013-06-24 15:14:01 -07:00
Doug Patti
7f1088054c Avoid variable scope collision with extensions
In #3031, an extensions variable was introduced with file-level scope
that defined the filetypes that CoffeeScript can compile. However, the
Module::load patching calls findExtension() which uses a local variable
called "extensions", which was overriding the outer level one and
causing getSourceMap() to fail.
2013-06-24 18:13:04 -04:00
Nami-Doc
ef5f58e30e Merge pull request #3045 from marchaefner/master
Fix path separator issues in tests.
2013-06-22 06:04:46 -07:00
Marc Häfner
13024e6911 Fix path separator issues in tests. 2013-06-22 14:57:23 +02:00
Nami-Doc
32e8e562ea Merge pull request #3043 from imcotton/reference-check
check existence of "path" for browser execution
2013-06-21 10:08:49 -07:00
Cotton Hou
7fdac5c3b9 check existence of "path" for browser execution 2013-06-22 00:58:30 +08:00
Jeremy Ashkenas
b68fd9d76e Merge pull request #3042 from marchaefner/issue2844
Avoid excessive AST traversal in `updateLocationDataIfMissing`.
2013-06-20 22:33:50 -07:00
Marc Häfner
25c6001a6c Speed up updateLocationDataIfMissing.
* Avoid excessive search for missing `locationData`
  * Fix `locationData` for `ELSE IF`.
2013-06-21 02:47:29 +02:00
Jeremy Ashkenas
7250fdd576 Merge pull request #3031 from alexgorbatchev/stack-trace-patch-optimizations-2
Stack trace patch optimizations
2013-06-16 02:37:33 -07:00
Jeremy Ashkenas
b7f8443052 Merge pull request #3034 from marchaefner/baseIndent
Better handling of initial indent at file start.
2013-06-16 02:35:06 -07:00
Marc Häfner
4fd5e9a3ab Better handling of initial indent at file start.
* Detect initial indentation before the first token and enforce it.
  * Don't add `INDENT` token (or the matching `OUTDENT, TERMINATOR`).
2013-06-14 00:28:45 +02:00
Alex Gorbatchev
3785996c44 Made stack patch test less brittle. 2013-06-13 13:50:05 -07:00
Alex Gorbatchev
eb0a222eea Using more standard convention for patched stack line numbers. 2013-06-13 12:58:04 -07:00
Alex Gorbatchev
3d761e73e3 Removed unnecessary source map generation during require() and made stack line number patching on by default. 2013-06-13 12:54:20 -07:00
Alex Gorbatchev
cc3b4e8080 Removed not used variable. 2013-06-13 12:23:06 -07:00
Michael Ficarra
ba7cb3ab69 fix #3029 2013-06-13 13:38:13 -05:00
Michael Ficarra
054443c46e rebuild #3029 2013-06-13 13:35:40 -05:00
Jeremy Ashkenas
c8dae22cb0 Merge pull request #3029 from wangxian/master
fix block comment format "\n"
2013-06-13 10:55:52 -07:00
木頭
183ec48308 fix block comment 2013-06-13 07:44:17 +08:00
Jeremy Ashkenas
13187b0199 Merge pull request #2856 from epidemian/issue2849
Fixes #2849: use correct filename and code in require()d sources
2013-06-09 00:40:18 -07:00
Demian Ferreiro
3c880bf601 Move a try/catch from compile to loadFile
This try/catch should only be necessary for dynamically loaded files. Also added a lengthier explanation of why this try/catch is needed.
2013-06-09 02:54:34 -03:00
Demian Ferreiro
8e90aaefc1 Merge branch 'master' into issue2849
Conflicts:
	lib/coffee-script/coffee-script.js
	src/coffee-script.coffee
2013-06-09 02:40:53 -03:00
Michael Ficarra
426ae97e49 Merge pull request #3014 from mset/master
base path for compilation can be './' as well as '.'
2013-06-03 06:27:38 -07:00
Marek Setnicka
f277a43645 Bug fix. When coffee is invoked with the -c parameter and './' value, first two characters of the directory into which the compiled files are stored get chopped off.
Example: if compilation invoked like this: 'coffee -o ../lib/ -cw ./', then
Source file: ./OutputFolder/file.coffee, compiled output: ./../lib/tputFolder/
The code only expected '.' to mark the local folder. However, './' is equally valid.
2013-06-03 13:51:26 +01:00
Jeremy Ashkenas
2e408648aa renaming import test files to avoid risking the disfavor of .gitignore 2013-06-02 10:57:18 +04:00
Jeremy Ashkenas
f2f10e85a8 Revert "remove cake build:ultraviolet"
This reverts commit 2e6a781014.
2013-06-02 09:45:11 +04:00
Michal Srb
e7ebdce60f Fix #2953. Method calls on splice endpoints 2013-04-23 05:42:37 +02:00
Michal Srb
fd61476106 Fix #1069. Non-callable literals shouldn't compile 2013-04-23 04:28:45 +02:00
Demian Ferreiro
d57b1aab10 Add power operator to the list of tokens that force a line continuation if they appear at the end of a line 2013-03-25 20:41:14 -03:00
Demian Ferreiro
22e8856b4d Add floor division // and modulo %% operators, and compound forms of the new operators
Also kill the empty regex :(
2013-03-25 03:19:05 -03:00
Demian Ferreiro
08b59aef8a Make power operator have higher precedence than unary operators: +, -, ~, ! 2013-03-25 00:05:04 -03:00
Demian Ferreiro
fbc019171c Make power operator compilation use proper AST nodes 2013-03-25 00:02:21 -03:00
Demian Ferreiro
e237abff84 Merge branch 'power-operator' of git://github.com/charliesome/coffee-script into more-math-operators
Conflicts:
	src/nodes.coffee
	test/operators.coffee
2013-03-24 22:47:46 -03:00
Demian Ferreiro
c0d1f22487 Add test for compiler errors on require()d files 2013-03-21 03:11:31 -03:00
Demian Ferreiro
67fd84fc1d Fixes #2849: now the compilation errors thrown by CoffeeScript.compile will include the correct filename and source code information 2013-03-19 04:27:34 -03:00
charliesome
3bd4dea305 fix the precedence test so it's actually meaningful 2012-01-11 21:37:06 +11:00
charliesome
a4249fd573 power operator + tests 2012-01-11 17:14:23 +11:00
129 changed files with 6056 additions and 2886 deletions

View File

@@ -1,5 +1,6 @@
fs = require 'fs'
path = require 'path'
_ = require 'underscore'
CoffeeScript = require './lib/coffee-script'
{spawn, exec} = require 'child_process'
helpers = require './lib/coffee-script/helpers'
@@ -41,6 +42,29 @@ run = (args, cb) ->
log = (message, color, explanation) ->
console.log color + message + reset + ' ' + (explanation or '')
codeFor = ->
counter = 0
hljs = require 'highlight.js'
hljs.configure classPrefix: ''
(file, executable = false, showLoad = true) ->
counter++
return unless fs.existsSync "documentation/js/#{file}.js"
cs = fs.readFileSync "documentation/coffee/#{file}.coffee", 'utf-8'
js = fs.readFileSync "documentation/js/#{file}.js", 'utf-8'
js = js.replace /^\/\/ generated.*?\n/i, ''
cshtml = "<pre><code>#{hljs.highlight('coffeescript', cs).value}</code></pre>"
jshtml = "<pre><code>#{hljs.highlight('javascript', js).value}</code></pre>"
append = if executable is yes then '' else "alert(#{executable});"
if executable and executable != yes
cs.replace /(\S)\s*\Z/m, "$1\n\nalert #{executable}"
run = if executable is true then 'run' else "run: #{executable}"
name = "example#{counter}"
script = "<script>window.#{name} = #{JSON.stringify cs}</script>"
load = if showLoad then "<div class='minibutton load' onclick='javascript: loadConsole(#{name});'>load</div>" else ''
button = if executable then "<div class='minibutton ok' onclick='javascript: #{js};#{append}'>#{run}</div>" else ''
"<div class='code'>#{cshtml}#{jshtml}#{script}#{load}#{button}<br class='clear' /></div>"
option '-p', '--prefix [DIR]', 'set the installation prefix for `cake install`'
task 'install', 'install CoffeeScript into /usr/local (or --prefix)', (options) ->
@@ -84,7 +108,6 @@ task 'build:parser', 'rebuild the Jison parser (run build first)', ->
parser = require('./lib/coffee-script/grammar').parser
fs.writeFile 'lib/coffee-script/parser.js', parser.generate()
task 'build:browser', 'rebuild the merged script for inclusion in the browser', ->
code = ''
for name in ['helpers', 'rewriter', 'lexer', 'parser', 'scope', 'nodes', 'sourcemap', 'coffee-script', 'browser']
@@ -118,8 +141,17 @@ task 'build:browser', 'rebuild the merged script for inclusion in the browser',
task 'doc:site', 'watch and continually rebuild the documentation for the website', ->
exec 'rake doc', (err) ->
throw err if err
source = 'documentation/index.html.js'
exec 'bin/coffee -bc -o documentation/js documentation/coffee/*.coffee'
do renderIndex = ->
codeSnippetCounter = 0
rendered = _.template fs.readFileSync(source, 'utf-8'), codeFor: codeFor()
fs.writeFileSync 'index.html', rendered
log "compiled", green, "#{source}"
fs.watchFile source, internal: 200, renderIndex
log "watching..." , green
task 'doc:source', 'rebuild the internal documentation', ->
@@ -155,6 +187,7 @@ task 'bench', 'quick benchmark of compilation time', ->
# Run the CoffeeScript test suite.
runTests = (CoffeeScript) ->
CoffeeScript.register()
startTime = Date.now()
currentFile = null
passedTests = 0

View File

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

15
README
View File

@@ -1,12 +1,11 @@
{
} } {
{ { } }
} }{ {
{ }{ } } _____ __ __
( }{ }{ { ) / ____| / _|/ _|
{ }{ }{ { } / ____| / _|/ _|
.- { { } { }} -. | | ___ | |_| |_ ___ ___
( ( } { } { } } ) | | / _ \| _| _/ _ \/ _ \
( { } { } { } } ) | | / _ \| _| _/ _ \/ _ \
|`-..________ ..-'| | |___| (_) | | | || __/ __/
| | \_____\___/|_| |_| \___|\___|
| ;--.
@@ -22,13 +21,13 @@
CoffeeScript is a little language that compiles into JavaScript.
Install Node.js, and then the CoffeeScript compiler:
sudo bin/cake install
Or, if you have the Node Package Manager installed:
If you have the Node Package Manager installed:
npm install -g coffee-script
(Leave off the -g if you don't wish to install globally.)
Or, if you don't wish to use npm:
sudo bin/cake install
Execute a script:
coffee /path/to/script.coffee
@@ -47,5 +46,5 @@
The source repository:
git://github.com/jashkenas/coffee-script.git
All contributors are listed here:
Top 100 contributors are listed here:
http://github.com/jashkenas/coffee-script/contributors

View File

@@ -1,79 +0,0 @@
require 'rubygems'
require 'erb'
require 'fileutils'
require 'rake/testtask'
require 'json'
desc "Build the documentation page"
task :doc do
source = 'documentation/index.html.erb'
child = fork { exec "bin/coffee -bcw -o documentation/js documentation/coffee/*.coffee" }
at_exit { Process.kill("INT", child) }
Signal.trap("INT") { exit }
loop do
mtime = File.stat(source).mtime
if !@mtime || mtime > @mtime
rendered = ERB.new(File.read(source)).result(binding)
File.open('index.html', 'w+') {|f| f.write(rendered) }
end
@mtime = mtime
sleep 1
end
end
desc "Build coffee-script-source gem"
task :gem do
require 'rubygems'
require 'rubygems/package'
gemspec = Gem::Specification.new do |s|
s.name = 'coffee-script-source'
s.version = JSON.parse(File.read('package.json'))["version"]
s.date = Time.now.strftime("%Y-%m-%d")
s.homepage = "http://jashkenas.github.com/coffee-script/"
s.summary = "The CoffeeScript Compiler"
s.description = <<-EOS
CoffeeScript is a little language that compiles into JavaScript.
Underneath all of those embarrassing braces and semicolons,
JavaScript has always had a gorgeous object model at its heart.
CoffeeScript is an attempt to expose the good parts of JavaScript
in a simple way.
EOS
s.files = [
'lib/coffee_script/coffee-script.js',
'lib/coffee_script/source.rb'
]
s.authors = ['Jeremy Ashkenas']
s.email = 'jashkenas@gmail.com'
s.rubyforge_project = 'coffee-script-source'
s.license = "MIT"
end
file = File.open("coffee-script-source.gem", "w")
Gem::Package.open(file, 'w') do |pkg|
pkg.metadata = gemspec.to_yaml
path = "lib/coffee_script/source.rb"
contents = <<-ERUBY
module CoffeeScript
module Source
def self.bundled_path
File.expand_path("../coffee-script.js", __FILE__)
end
end
end
ERUBY
pkg.add_file_simple(path, 0644, contents.size) do |tar_io|
tar_io.write(contents)
end
contents = File.read("extras/coffee-script.js")
path = "lib/coffee_script/coffee-script.js"
pkg.add_file_simple(path, 0644, contents.size) do |tar_io|
tar_io.write(contents)
end
end
end

View File

@@ -0,0 +1,8 @@
$ 'body'
.click (e) ->
$ '.box'
.fadeIn 'fast'
.addClass '.active'
.css 'background', 'white'

View File

@@ -2,3 +2,5 @@ class Person
constructor: (options) ->
{@name, @age, @height} = options
tim = new Person age: 4

View File

@@ -0,0 +1,7 @@
text = "Every literary critic believes he will
outwit history and have the last word"
[first, ..., last] = text.split " "

View File

@@ -2,8 +2,8 @@ numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
start = numbers[0..2]
middle = numbers[3...6]
middle = numbers[3...-2]
end = numbers[6..]
end = numbers[-2..]
copy = numbers[..]

View File

@@ -1,8 +1,6 @@
mobyDick = "Call me Ishmael. Some years ago --
never mind how long precisely -- having little
or no money in my purse, and nothing particular
to interest me on shore, I thought I would sail
about a little and see the watery part of the
world..."
never mind how long precisely -- having little
or no money in my purse, and nothing particular
to interest me on shore, I thought I would sail
about a little and see the watery part of the
world..."

View File

@@ -1,64 +0,0 @@
pre.idle .InheritedClass {
}
pre.idle .TypeName {
color: #21439C;
}
pre.idle .Number {
}
pre.idle .LibraryVariable {
color: #A535AE;
}
pre.idle .Storage {
color: #FF5600;
}
pre.idle .line-numbers {
background-color: #BAD6FD;
color: #000000;
}
pre.idle {
background-color: #FFFFFF;
color: #000000;
}
pre.idle .StringInterpolation {
color: #990000;
}
pre.idle .TagName {
}
pre.idle .LibraryConstant {
color: #A535AE;
}
pre.idle .FunctionArgument {
color: #0076ad;
}
pre.idle .BuiltInConstant {
color: #A535AE;
}
pre.idle .Invalid {
background-color: #990000;
color: #FFFFFF;
}
pre.idle .LibraryClassType {
color: #A535AE;
}
pre.idle .LibraryFunction {
color: #A535AE;
}
pre.idle .TagAttribute {
}
pre.idle .Keyword {
color: #FF5600;
}
pre.idle .UserDefinedConstant {
}
pre.idle .String {
color: #00A33F;
}
pre.idle .FunctionName {
color: #21439C;
}
pre.idle .Variable {
color: #A535AE;
}
pre.idle .Comment {
color: #919191;
}

View File

@@ -0,0 +1,66 @@
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original code:; http://softwaremaniacs.org/media/soft/highlight/styles/tomorrow.css */
/* But forked for CoffeeScript */
.tomorrow-comment, pre .comment, pre .title {
color: #8e908c;
}
.tomorrow-red, pre .variable, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo {
color: #c82829;
}
.tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .params, pre .constant {
color: #000000;
}
.tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute {
color: #eab700;
}
.tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata {
color: #718c00;
}
.tomorrow-aqua, pre .css .hexcolor {
color: #3e999f;
}
.tomorrow-blue, pre .function, pre .function .title, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title {
color: #21439C;
}
.tomorrow-purple, pre .keyword, pre .reserved, pre .javascript .function {
color: #FF5600;
}
pre .subst {
color: #A535AE;
}
pre .literal {
color: #A535AE;
}
pre .property {
color: #A535AE;
}
pre .class .title {
color: #21439C;
}
pre code {
display: block;
background: white;
color: #000000;
}
pre .coffeescript .javascript,
pre .javascript .xml,
pre .tex .formula,
pre .xml .javascript,
pre .xml .vbscript,
pre .xml .css,
pre .xml .cdata {
opacity: 0.5;
}

File diff suppressed because one or more lines are too long

View File

@@ -1,29 +1,3 @@
<%
require 'uv'
require 'json'
@counter = 0
def code_for(file, executable=false, show_load=true)
@counter += 1
return '' unless File.exists?("documentation/js/#{file}.js")
cs = File.read("documentation/coffee/#{file}.coffee")
js = File.read("documentation/js/#{file}.js")
js = js.sub(/^\/\/ generated.*?\n/i, '')
cshtml = Uv.parse(cs, 'xhtml', 'coffeescript', false, 'idle', false)
jshtml = Uv.parse(js, 'xhtml', 'javascript', false, 'idle', false)
append = executable == true ? '' : "alert(#{executable});"
if executable and executable != true
cs.sub!(/(\S)\s*\Z/m, "\\1\n\nalert #{executable}")
end
run = executable == true ? 'run' : "run: #{executable}"
name = "example#{@counter}"
script = "<script>window.#{name} = #{cs.to_json}</script>"
import = show_load ? "<div class='minibutton load' onclick='javascript: loadConsole(#{name});'>load</div>" : ''
button = executable ? "<div class='minibutton ok' onclick='javascript: #{js};#{append}'>#{run}</div>" : ''
"<div class='code'>#{cshtml}#{jshtml}#{script}#{import}#{button}<br class='clear' /></div>"
end
%>
<!DOCTYPE html>
<html>
<head>
@@ -31,7 +5,7 @@
<title>CoffeeScript</title>
<link rel="canonical" href="http://coffeescript.org" />
<link rel="stylesheet" type="text/css" href="documentation/css/docs.css" />
<link rel="stylesheet" type="text/css" href="documentation/css/idle.css" />
<link rel="stylesheet" type="text/css" href="documentation/css/tomorrow.css" />
<link rel="shortcut icon" href="documentation/images/favicon.ico" />
</head>
<body>
@@ -137,7 +111,7 @@
<p>
<b>Latest Version:</b>
<a href="http://github.com/jashkenas/coffee-script/tarball/1.6.3">1.6.3</a>
<a href="http://github.com/jashkenas/coffee-script/tarball/1.7.0">1.7.0</a>
</p>
<pre>
@@ -150,7 +124,7 @@ sudo npm install -g coffee-script</pre>
<p><i>CoffeeScript on the left, compiled JavaScript output on the right.</i></p>
<%= code_for('overview', 'cubes', false) %>
<%= codeFor('overview', 'cubes', false) %>
<h2>
<span id="installation" class="bookmark"></span>
@@ -435,12 +409,12 @@ Expressions
an arrow, and the function body. The empty function looks like this:
<tt>-></tt>
</p>
<%= code_for('functions', 'cube(5)') %>
<%= codeFor('functions', 'cube(5)') %>
<p>
Functions may also have default values for arguments. Override the default
value by passing a non-null argument.
Functions may also have default values for arguments, which will be used
if the incoming argument is missing (<tt>null</tt> or <tt>undefined</tt>).
</p>
<%= code_for('default_args', 'fill("cup")') %>
<%= codeFor('default_args', 'fill("cup")') %>
<p>
<span id="objects_and_arrays" class="bookmark"></span>
@@ -450,14 +424,14 @@ Expressions
the commas are optional. Objects may be created using indentation instead
of explicit braces, similar to <a href="http://yaml.org">YAML</a>.
</p>
<%= code_for('objects_and_arrays', 'song.join(" ... ")') %>
<%= codeFor('objects_and_arrays', 'song.join(" ... ")') %>
<p>
In JavaScript, you can't use reserved words, like <tt>class</tt>, 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 don't have to worry
about it (say, when using jQuery).
</p>
<%= code_for('objects_reserved') %>
<%= codeFor('objects_reserved') %>
<p>
<span id="lexical-scope" class="bookmark"></span>
@@ -466,7 +440,7 @@ Expressions
are properly declared within lexical scope &mdash; you never need to write
<tt>var</tt> yourself.
</p>
<%= code_for('scope', 'inner') %>
<%= codeFor('scope', 'inner') %>
<p>
Notice how all of the variable declarations have been pushed up to
the top of the closest scope, the first time they appear.
@@ -511,7 +485,7 @@ Expressions
is no explicit ternary statement in CoffeeScript &mdash; you simply use
a regular <b>if</b> statement on a single line.
</p>
<%= code_for('conditionals') %>
<%= codeFor('conditionals') %>
<p>
<span id="splats" class="bookmark"></span>
@@ -521,7 +495,7 @@ Expressions
splats <tt>...</tt>, both for function definition as well as invocation,
making variable numbers of arguments a little bit more palatable.
</p>
<%= code_for('splats', true) %>
<%= codeFor('splats', true) %>
<p>
<span id="loops" class="bookmark"></span>
@@ -532,7 +506,7 @@ Expressions
Unlike for loops, array comprehensions are expressions, and can be returned
and assigned.
</p>
<%= code_for('array_comprehensions') %>
<%= codeFor('array_comprehensions') %>
<p>
Comprehensions should be able to handle most places where you otherwise
would use a loop, <b>each</b>/<b>forEach</b>, <b>map</b>, or <b>select</b>/<b>filter</b>, for example:
@@ -541,7 +515,7 @@ Expressions
in fixed-size increments, you can use a range to specify the start and
end of your comprehension.
</p>
<%= code_for('range_comprehensions', 'countdown') %>
<%= codeFor('range_comprehensions', 'countdown') %>
<p>
Note how because we are assigning the value of the comprehensions to a
variable in the example above, CoffeeScript is collecting the result of
@@ -561,7 +535,7 @@ Expressions
an object. Use <tt>of</tt> to signal comprehension over the properties of
an object instead of the values in an array.
</p>
<%= code_for('object_comprehensions', 'ages.join(", ")') %>
<%= codeFor('object_comprehensions', 'ages.join(", ")') %>
<p>
If you would like to iterate over just the keys that are defined on the
object itself, by adding a <tt>hasOwnProperty</tt>
@@ -574,7 +548,7 @@ Expressions
as an expression, returning an array containing the result of each iteration
through the loop.
</p>
<%= code_for('while', 'lyrics.join("\n")') %>
<%= codeFor('while', 'lyrics.join("\n")') %>
<p>
For readability, the <b>until</b> keyword is equivalent to <tt>while not</tt>,
and the <b>loop</b> keyword is equivalent to <tt>while true</tt>.
@@ -586,7 +560,7 @@ Expressions
provides the <tt>do</tt> keyword, which immediately invokes a passed function,
forwarding any arguments.
</p>
<%= code_for('do') %>
<%= codeFor('do') %>
<p>
<span id="slices" class="bookmark"></span>
@@ -597,12 +571,12 @@ Expressions
Slices indices have useful defaults. An omitted first index defaults to
zero and an omitted second index defaults to the size of the array.
</p>
<%= code_for('slices', 'middle') %>
<%= codeFor('slices', 'middle') %>
<p>
The same syntax can be used with assignment to replace a segment of an array
with new values, splicing it.
</p>
<%= code_for('splices', 'numbers') %>
<%= codeFor('splices', 'numbers') %>
<p>
Note that JavaScript strings are immutable, and can't be spliced.
</p>
@@ -616,7 +590,7 @@ Expressions
pushed down into each possible branch of execution in the function
below.
</p>
<%= code_for('expressions', 'eldest') %>
<%= codeFor('expressions', 'eldest') %>
<p>
Even though functions will always return their final value, it's both possible
and encouraged to return early from a function body writing out the explicit
@@ -626,19 +600,19 @@ Expressions
Because variable declarations occur at the top of scope, assignment can
be used within expressions, even for variables that haven't been seen before:
</p>
<%= code_for('expressions_assignment', 'six') %>
<%= codeFor('expressions_assignment', 'six') %>
<p>
Things that would otherwise be statements in JavaScript, when used
as part of an expression in CoffeeScript, are converted into expressions
by wrapping them in a closure. This lets you do useful things, like assign
the result of a comprehension to a variable:
</p>
<%= code_for('expressions_comprehension', 'globals') %>
<%= codeFor('expressions_comprehension', 'globals') %>
<p>
As well as silly things, like passing a <b>try/catch</b> statement directly
into a function call:
</p>
<%= code_for('expressions_try', true) %>
<%= codeFor('expressions_try', true) %>
<p>
There are a handful of statements in JavaScript that can't be meaningfully
converted into expressions, namely <tt>break</tt>, <tt>continue</tt>,
@@ -682,7 +656,9 @@ Expressions
You can use <tt>in</tt> to test for array presence, and <tt>of</tt> to
test for JavaScript object-key presence.
</p>
<p>
To simplify math expressions, `**` can be used for exponentiation, `//` performs integer division and `%%` provides true mathematical modulo.
</p>
<p>
All together now:
</p>
@@ -699,9 +675,12 @@ Expressions
<tr><td><tt>@, this</tt></td><td><tt>this</tt></td></tr>
<tr><td><tt>of</tt></td><td><tt>in</tt></td></tr>
<tr><td><tt>in</tt></td><td><i><small>no JS equivalent</small></i></td></tr>
<tr><td><tt>a ** b</tt></td><td><tt>Math.pow(a, b)</tt></td></tr>
<tr><td><tt>a // b</tt></td><td><tt>Math.floor(a / b)</tt></td></tr>
<tr><td><tt>a %% b</tt></td><td><tt>(a % b + b) % b</tt></td></tr>
</table>
<%= code_for('aliases') %>
<%= codeFor('aliases') %>
<p>
<b class="header">The Existential Operator</b>
@@ -715,7 +694,7 @@ Expressions
It can also be used for safer conditional assignment than <tt>||=</tt>
provides, for cases where you may be handling numbers or strings.
</p>
<%= code_for('existence', 'footprints') %>
<%= codeFor('existence', 'footprints') %>
<p>
The accessor variant of the existential operator <tt>?.</tt> can be used to soak
up null references in a chain of properties. Use it instead
@@ -724,7 +703,7 @@ Expressions
result, if the chain is broken, <b>undefined</b> is returned instead of
the <b>TypeError</b> that would be raised otherwise.
</p>
<%= code_for('soaks') %>
<%= codeFor('soaks') %>
<p>
Soaking up nulls is similar to Ruby's
<a href="http://andand.rubyforge.org/">andand gem</a>, and to the
@@ -757,7 +736,7 @@ Expressions
Constructor functions are named, to better support helpful stack traces.
In the first class in the example below, <tt>this.constructor.name is "Animal"</tt>.
</p>
<%= code_for('classes', true) %>
<%= codeFor('classes', true) %>
<p>
If structuring your prototypes classically isn't your cup of tea, CoffeeScript
provides a couple of lower-level conveniences. The <tt>extends</tt> operator
@@ -766,7 +745,7 @@ Expressions
quick access to an object's prototype; and <tt>super()</tt>
is converted into a call against the immediate ancestor's method of the same name.
</p>
<%= code_for('prototypes', '"one_two".dasherize()') %>
<%= codeFor('prototypes', '"one_two".dasherize()') %>
<p>
Finally, class definitions are blocks of executable code, which make for interesting
metaprogramming possibilities. Because in the context of a class definition,
@@ -786,26 +765,30 @@ Expressions
on the right to the variables on the left. In the simplest case, it can be
used for parallel assignment:
</p>
<%= code_for('parallel_assignment', 'theBait') %>
<%= codeFor('parallel_assignment', 'theBait') %>
<p>
But it's also helpful for dealing with functions that return multiple
values.
</p>
<%= code_for('multiple_return_values', 'forecast') %>
<%= codeFor('multiple_return_values', 'forecast') %>
<p>
Destructuring assignment can be used with any depth of array and object nesting,
to help pull out deeply nested properties.
</p>
<%= code_for('object_extraction', 'name + "" + street') %>
<%= codeFor('object_extraction', '"name + "-" + street"') %>
<p>
Destructuring assignment can even be combined with splats.
</p>
<%= code_for('patterns_and_splats', 'contents.join("")') %>
<%= codeFor('patterns_and_splats', 'contents.join("")') %>
<p>
Expansion can be used to retrieve elements from the end of an array without having to assign the rest of its values. It works in function parameter lists as well.
</p>
<%= codeFor('expansion', '"first + " " + last"') %>
<p>
Destructuring assignment is also useful when combined with class constructors
to assign properties to your instance from an options object passed to the constructor.
</p>
<%= code_for('constructor_destructuring', 'contents.join("")') %>
<%= codeFor('constructor_destructuring', 'tim.age') %>
<p>
<span id="fat-arrow" class="bookmark"></span>
@@ -825,7 +808,7 @@ Expressions
to use with <tt>bind</tt>. Functions created with the fat arrow are able to access
properties of the <tt>this</tt> where they're defined.
</p>
<%= code_for('fat_arrow') %>
<%= codeFor('fat_arrow') %>
<p>
If we had used <tt>-&gt;</tt> in the callback above, <tt>@customer</tt> would
have referred to the undefined "customer" property of the DOM element,
@@ -844,7 +827,7 @@ Expressions
snippets of JavaScript within your CoffeeScript, you can
use backticks to pass it straight through.
</p>
<%= code_for('embedded', 'hi()') %>
<%= codeFor('embedded', 'hi()') %>
<p>
<span id="switch" class="bookmark"></span>
@@ -861,12 +844,12 @@ Expressions
values for each <b>when</b> clause. If any of the values match, the clause
runs.
</p>
<%= code_for('switch') %>
<%= codeFor('switch') %>
<p>
Switch statements can also be used without a control expression, turning them in to a cleaner alternative to if/else chains.
</p>
<%= code_for('switch_with_no_expression') %>
<%= codeFor('switch_with_no_expression') %>
<p>
<span id="try" class="bookmark"></span>
@@ -874,7 +857,7 @@ Expressions
Try/catch statements are just about the same as JavaScript (although
they work as expressions).
</p>
<%= code_for('try') %>
<%= codeFor('try') %>
<p>
<span id="comparisons" class="bookmark"></span>
@@ -884,7 +867,7 @@ Expressions
from Python &mdash; making it easy to test if a value falls within a
certain range.
</p>
<%= code_for('comparisons', 'healthy') %>
<%= codeFor('comparisons', 'healthy') %>
<p>
<span id="strings" class="bookmark"></span>
@@ -893,18 +876,18 @@ Expressions
strings allow for interpolated values, using <tt>#{ ... }</tt>,
and single-quoted strings are literal.
</p>
<%= code_for('interpolation', 'sentence') %>
<%= codeFor('interpolation', 'sentence') %>
<p>
Multiline strings are allowed in CoffeeScript.
Multiline strings are allowed in CoffeeScript. Lines are joined by a single space unless they end with a backslash. Indentation is ignored.
</p>
<%= code_for('strings', 'mobyDick') %>
<%= codeFor('strings', 'mobyDick') %>
<p>
Block strings can be used to hold formatted or indentation-sensitive text
(or, if you just don't feel like escaping quotes and apostrophes). The
indentation level that begins the block is maintained throughout, so
you can keep it all aligned with the body of your code.
</p>
<%= code_for('heredocs', 'html') %>
<%= codeFor('heredocs', 'html') %>
<p>
Double-quoted block strings, like other double-quoted strings, allow interpolation.
</p>
@@ -914,7 +897,7 @@ Expressions
the top of a file. Block comments, which mirror the syntax for block strings,
are preserved in the generated code.
</p>
<%= code_for('block_comment') %>
<%= codeFor('block_comment') %>
<p>
<span id="regexes" class="bookmark"></span>
@@ -925,7 +908,7 @@ Expressions
block regexes are delimited by <tt>///</tt> and go a long way towards making complex
regular expressions readable. To quote from the CoffeeScript source:
</p>
<%= code_for('heregexes') %>
<%= codeFor('heregexes') %>
<h2>
@@ -951,7 +934,7 @@ Expressions
be made available in the <tt>options</tt> object. Here's a task that uses
the Node.js API to rebuild CoffeeScript's parser:
</p>
<%= code_for('cake_tasks') %>
<%= codeFor('cake_tasks') %>
<p>
If you need to invoke one task before another &mdash; for example, running
<tt>build</tt> before <tt>test</tt>, you can use the <tt>invoke</tt> function:
@@ -1059,6 +1042,16 @@ Expressions
is a succinct and freely downloadable guide to building testable
applications with CoffeeScript and Jasmine.
</li>
<li>
<a href="http://www.packtpub.com/coffeescript-application-development/book">CoffeeScript Application Development</a>
is a new book from Packt Publishing that introduces CoffeeScript while
walking through the process of building a demonstration web application.
</li>
<li>
<a href="http://www.manning.com/lee/">CoffeeScript in Action</a>
is a new book from Manning Publications that covers CoffeeScript syntax, composition techniques
and application development.
</li>
</ul>
<h2>
@@ -1198,6 +1191,47 @@ Expressions
Change Log
</h2>
<p>
<b class="header" style="margin-top: 20px;">
<a href="https://github.com/jashkenas/coffee-script/compare/1.6.3...1.7.0">1.7.0</a>
<span class="timestamp"> &ndash; <small>January 28, 2014</small></span>
</b>
<ul>
<li>
When requiring CoffeeScript files in Node you must now explicitly register the compiler. This can be done with <tt>require 'coffee-script/register'</tt> or <tt>CoffeeScript.register()</tt>. Also for configuration such as Mocha's, use <b>coffee-script/register</b>.
</li>
<li>
Improved error messages, source maps and stack traces. Source maps now use the updated <tt>//#</tt> syntax.
</li>
<li>
Leading <tt>.</tt> now closes all open calls, allowing for simpler chaining syntax.
</li>
</ul>
<%= codeFor('chaining') %>
<ul>
<li>
Added <tt>**</tt>, <tt>//</tt> and <tt>%%</tt> operators and <tt>...</tt> expansion in paramater lists and destructuring expressions.
</li>
<li>
Multiline strings are now joined by a single space and ignore all indentation. A backslash at the end of a line can denote the amount of whitespace between lines, in both strings and heredocs. Backslashes correctly escape whitespace in block regexes.
</li>
<li>
Closing brackets can now be indented and therefore no longer cause unexpected error.
</li>
<li>
Several breaking compilation fixes. Non-callable literals (strings, numbers etc.) don't compile in a call now and multiple postfix conditionals compile properly. Postfix conditionals and loops always bind object literals. Conditional assignment compiles properly in subexpressions. <tt>super</tt> is disallowed outside of methods and works correctly inside <tt>for</tt> loops.
</li>
<li>
Formatting of compiled block comments has been improved.
</li>
<li>
No more <tt>-p</tt> folders on Windows.
</li>
<li>
The <tt>options</tt> object passed to CoffeeScript is no longer mutated.
</li>
</ul>
</p>
<p>
<b class="header" style="margin-top: 20px;">
<a href="https://github.com/jashkenas/coffee-script/compare/1.6.2...1.6.3">1.6.3</a>

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var volume, winner;
if (ignition === true) {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var courses, dish, food, foods, i, _i, _j, _k, _len, _len1, _len2, _ref;
_ref = ['toast', 'cheese', 'wine'];

View File

@@ -1,7 +1,7 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
/*
SkinnyMochaHalfCaffScript Compiler v1.0
Released under the MIT License
*/
*/

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var fs;
fs = require('fs');

View File

@@ -0,0 +1,4 @@
// Generated by CoffeeScript 1.7.0
$('body').click(function(e) {
return $('.box').fadeIn('fast').addClass('.active');
}).css('background', 'white');

View File

@@ -1,5 +1,5 @@
// Generated by CoffeeScript 1.6.3
var Animal, Horse, Snake, sam, tom, _ref, _ref1,
// Generated by CoffeeScript 1.7.0
var Animal, Horse, Snake, sam, tom,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
@@ -20,8 +20,7 @@ Snake = (function(_super) {
__extends(Snake, _super);
function Snake() {
_ref = Snake.__super__.constructor.apply(this, arguments);
return _ref;
return Snake.__super__.constructor.apply(this, arguments);
}
Snake.prototype.move = function() {
@@ -37,8 +36,7 @@ Horse = (function(_super) {
__extends(Horse, _super);
function Horse() {
_ref1 = Horse.__super__.constructor.apply(this, arguments);
return _ref1;
return Horse.__super__.constructor.apply(this, arguments);
}
Horse.prototype.move = function() {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var cholesterol, healthy;
cholesterol = 127;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var date, mood;
if (singing) {

View File

@@ -1,5 +1,5 @@
// Generated by CoffeeScript 1.6.3
var Person;
// Generated by CoffeeScript 1.7.0
var Person, tim;
Person = (function() {
function Person(options) {
@@ -9,3 +9,7 @@ Person = (function() {
return Person;
})();
tim = new Person({
age: 4
});

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var fill;
fill = function(container, liquid) {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var filename, _fn, _i, _len;
_fn = function(filename) {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var hi;
hi = function() {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var footprints, solipsism, speed;
if ((typeof mind !== "undefined" && mind !== null) && (typeof world === "undefined" || world === null)) {

View File

@@ -0,0 +1,6 @@
// Generated by CoffeeScript 1.7.0
var first, last, text, _ref;
text = "Every literary critic believes he will outwit history and have the last word";
_ref = text.split(" "), first = _ref[0], last = _ref[_ref.length - 1];

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var eldest, grade;
grade = function(student) {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var one, six, three, two;
six = (one = 1) + (two = 2) + (three = 3);

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var globals, name;
globals = ((function() {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var error;
alert((function() {

View File

@@ -1,11 +1,12 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var Account;
Account = function(customer, cart) {
var _this = this;
this.customer = customer;
this.cart = cart;
return $('.shopping_cart').bind('click', function(event) {
return _this.customer.purchase(_this.cart);
});
return $('.shopping_cart').bind('click', (function(_this) {
return function(event) {
return _this.customer.purchase(_this.cart);
};
})(this));
};

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var cube, square;
square = function(x) {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var html;
html = "<strong>\n cup of coffeescript\n</strong>";

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var OPERATOR;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var author, quote, sentence;
author = "Wittgenstein";

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var city, forecast, temp, weatherReport, _ref;
weatherReport = function(location) {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var age, ages, child, yearsOld;
yearsOld = {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var city, futurists, name, street, _ref, _ref1;
futurists = {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var bitlist, kids, singers, song;
song = ["do", "re", "mi", "fa", "so"];

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
$('.account').attr({
"class": 'active'
});

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var cubes, list, math, num, number, opposite, race, square,
__slice = [].slice;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var theBait, theSwitch, _ref;
theBait = 1000;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var close, contents, open, tag, _i, _ref,
__slice = [].slice;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
String.prototype.dasherize = function() {
return this.replace(/_/g, "-");
};

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var countdown, num;
countdown = (function() {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var changeNumbers, inner, outer;
outer = 1;

View File

@@ -1,12 +1,12 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var copy, end, middle, numbers, start;
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
start = numbers.slice(0, 3);
middle = numbers.slice(3, 6);
middle = numbers.slice(3, -2);
end = numbers.slice(6);
end = numbers.slice(-2);
copy = numbers.slice(0);

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var zip, _ref;
zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var awardMedals, contenders, gold, rest, silver,
__slice = [].slice;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var numbers, _ref;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var mobyDick;
mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...";

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
switch (day) {
case "Mon":
go(work);

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var grade, score;
score = 76;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var error;
try {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
var lyrics, num;
if (this.studyingEconomics) {
@@ -16,7 +16,7 @@ lyrics = (function() {
var _results;
_results = [];
while (num -= 1) {
_results.push("" + num + " little monkeys, jumping on the bed. One fell out and bumped his head.");
_results.push("" + num + " little monkeys, jumping on the bed. One fell out and bumped his head.");
}
return _results;
})();

View File

@@ -31,7 +31,7 @@ File.open = (path, mode, block) ->
# Write.
write = (location, data) ->
path = new Pathname location
throw new Error "Location does not exist" unless fs.existsSync(location)
throw new Error "Location does not exist" unless fs.existsSync location
File.open path, 'w', (file) ->
return false if Digest.MD5.hexdigest(file.read()) is data.hash()

View File

@@ -13,7 +13,7 @@ run_loop = ->
wait()
# Objects:
dense_object_literal = {one: 1, two: 2, three: 3}
dense_object_literal = one: 1, two: 2, three: 3
spaced_out_multiline_object =
pi: 3.14159
@@ -56,7 +56,7 @@ race = ->
run()
walk()
crawl()
if tired then return sleep()
return sleep() if tired
race()
# Conditional assignment:
@@ -64,7 +64,7 @@ good or= evil
wine and= cheese
# Nested property access and calls.
((moon.turn(360))).shapes[3].move({x: 45, y: 30}).position['top'].offset('x')
(moon.turn 360).shapes[3].move(x: 45, y: 30).position['top'].offset('x')
a = b = c = 5
@@ -79,7 +79,7 @@ try
dogs_and_cats_living_together()
throw "up"
catch error
print(error)
print error
finally
clean_up()
@@ -130,8 +130,8 @@ wednesday = -> eat_breakfast(); go_to_work(); eat_dinner()
# Multiline strings with inner quotes.
story = "Lorem ipsum dolor \"sit\" amet, consectetuer adipiscing elit,
sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad."
sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad."
# Inheritance and calling super.
class Animal

View File

@@ -19,7 +19,7 @@ binary_search = (items, value) ->
# Test the function.
console.log 2 is binary_search [10, 20, 30, 40, 50], 30
console.log 4 is binary_search [-97, 35, 67, 88, 1200], 1200
console.log 0 is binary_search [0, 45, 70], 0
console.log(-1 is binary_search [0, 45, 70], 10)
console.log 2 is binary_search [10, 20, 30, 40, 50], 30
console.log 4 is binary_search [-97, 35, 67, 88, 1200], 1200
console.log 0 is binary_search [0, 45, 70], 0
console.log -1 is binary_search [0, 45, 70], 10

View File

@@ -1,8 +1,8 @@
# A bubble sort implementation, sorting the given array in-place.
bubble_sort = (list) ->
for i in [0...list.length]
for j in [0...list.length - i]
[list[j], list[j+1]] = [list[j+1], list[j]] if list[j] > list[j+1]
for j in [0...list.length - i] when list[j] > list[j + 1]
[list[j], list[j+1]] = [list[j + 1], list[j]]
list

View File

@@ -1,8 +1,8 @@
# "Classic" linked list implementation that doesn't keep track of its size.
class LinkedList
->
this._head = null # Pointer to the first item in the list.
constructor: ->
@_head = null # Pointer to the first item in the list.
# Appends some data to the end of the list. This method traverses the existing
@@ -12,10 +12,10 @@ class LinkedList
# Create a new node object to wrap the data.
node = data: data, next: null
current = this._head or= node
current = @_head or= node
if this._head isnt node
(current = current.next) while current.next
if @_head isnt node
current = current.next while current.next
current.next = node
this
@@ -27,11 +27,11 @@ class LinkedList
# Check for out-of-bounds values.
return null if index < 0
current = this._head or null
current = @_head or null
i = -1
# Advance through the list.
(current = current.next) while current and index > (i += 1)
current = current.next while current and index > ++i
# Return null if we've reached the end.
current and current.data
@@ -43,16 +43,16 @@ class LinkedList
# Check for out-of-bounds values.
return null if index < 0
current = this._head or null
current = @_head or null
i = -1
# Special case: removing the first item.
if index is 0
this._head = current.next
@_head = current.next
else
# Find the right location.
([previous, current] = [current, current.next]) while index > (i += 1)
[previous, current] = [current, current.next] while index > ++i
# Skip over the item to remove.
previous.next = current.next
@@ -63,7 +63,7 @@ class LinkedList
# Calculate the number of items in the list.
size: ->
current = this._head
current = @_head
count = 0
while current
@@ -76,7 +76,7 @@ class LinkedList
# Convert the list into an array.
toArray: ->
result = []
current = this._head
current = @_head
while current
result.push current.data
@@ -86,7 +86,7 @@ class LinkedList
# The string representation of the linked list.
toString: -> this.toArray().toString()
toString: -> @toArray().toString()
# Tests.

View File

@@ -7,13 +7,13 @@ is_valid_identifier = (identifier) ->
sum = 0
alt = false
for i in [identifier.length - 1..0] by -1
for c in identifier by -1
# Get the next digit.
num = parseInt identifier.charAt(i), 10
num = parseInt c, 10
# If it's not a valid number, abort.
return false if isNaN(num)
return false if isNaN num
# If it's an alternate number...
if alt

View File

@@ -3,13 +3,12 @@ merge_sort = (list) ->
return list if list.length is 1
result = []
pivot = Math.floor list.length / 2
left = merge_sort list.slice 0, pivot
right = merge_sort list.slice pivot
while left.length and right.length
result.push(if left[0] < right[0] then left.shift() else right.shift())
result = while left.length and right.length
if left[0] < right[0] then left.shift() else right.shift()
result.concat(left).concat(right)

View File

@@ -1,8 +1,11 @@
# Examples from the Poignant Guide.
# These are examples of syntax differences between CoffeeScript and Ruby,
# they won't run.
# ['toast', 'cheese', 'wine'].each { |food| print food.capitalize }
['toast', 'wine', 'cheese'].each (food) -> print food.capitalize()
print food.capitalize() for food in ['toast', 'wine', 'cheese']
@@ -14,10 +17,10 @@
# end
LotteryTicket =
get_picks: -> @picks
set_picks: (@picks) ->
get_purchased: -> @purchase
set_purchased: (@purchased) ->
get_picks: -> @picks
set_picks: (@picks) ->
get_purchased: -> @purchase
set_purchased: (@purchased) ->
@@ -42,13 +45,10 @@ LotteryDraw =
play: ->
result = LotteryTicket.new_random()
winners = {}
this.tickets.each (buyer, ticket_list) ->
ticket_list.each (ticket) ->
score = ticket.score result
return if score is 0
winners[buyer] or= []
winners[buyer].push [ticket, score]
this.tickets = {}
for buyer, ticketList of @tickets
for ticket in ticketList when (score = ticket.score result) isnt 0
(winners[buyer] or= []).push [ticket, score]
@tickets = {}
winners
@@ -64,7 +64,7 @@ LotteryDraw =
WishScanner =
scan_for_a_wish: ->
wish = this.read().detect (thought) -> thought.index('wish: ') is 0
wish = @read().detect (thought) -> thought.indexOf('wish: ') is 0
wish.replace 'wish: ', ''
@@ -109,28 +109,28 @@ Creature =
# This method applies a hit taken during a fight.
hit: (damage) ->
p_up = Math.rand this.charisma
p_up = Math.rand @charisma
if p_up % 9 is 7
this.life += p_up / 4
console.log "[" + this.name + " magick powers up " + p_up + "!]"
this.life -= damage
if this.life <= 0 then console.log "[" + this.name + " has died.]"
@life += p_up / 4
console.log "[#{@name} magick powers up #{p_up}!]"
@life -= damage
if @life <= 0 then console.log "[#{@name} has died.]"
# This method takes one turn in a fight.
fight: (enemy, weapon) ->
if this.life <= 0 then return console.log "[" + this.name + "is too dead to fight!]"
return console.log "[#{@name} is too dead to fight!]" if @life <= 0
# Attack the opponent.
your_hit = Math.rand this.strength + weapon
console.log "[You hit with " + your_hit + "points of damage!]"
your_hit = Math.rand @strength + weapon
console.log "[You hit with #{your_hit}points of damage!]"
enemy.hit your_hit
# Retaliation.
console.log enemy
if enemy.life > 0
enemy_hit = Math.rand enemy.strength + enemy.weapon
console.log "[Your enemy hit with " + enemy_hit + "points of damage!]"
this.hit enemy_hit
console.log "[Your enemy hit with #{enemy_hit}points of damage!]"
@hit enemy_hit
@@ -156,7 +156,7 @@ code_words.each (real, code) -> idea.replace(real, code)
# Save the jibberish to a new file
print "File encoded. Please enter a name for this idea: "
idea_name = gets().strip()
File.open "idea-" + idea_name + '.txt', 'w', (file) -> file.write idea
File.open "idea-#{idea_name}.txt", 'w', (file) -> file.write idea
@@ -174,8 +174,8 @@ File.open "idea-" + idea_name + '.txt', 'w', (file) -> file.write idea
wipe_mutterings_from = (sentence) ->
throw new Error "cannot wipe mutterings" unless sentence.indexOf
while sentence.indexOf('(') >= 0
open = sentence.indexOf('(') - 1
close = sentence.indexOf(')') + 1
sentence = sentence.slice(0, open) + sentence.slice(close, sentence.length)
while '(' in sentence
open = sentence.indexOf('(')
close = sentence.indexOf(')')
sentence = "#{sentence[0...open]}#{sentence[close + 1..]}"
sentence

View File

@@ -45,7 +45,7 @@ foods[2]
# (key, ' is a ', val) join print.
for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
print key + ' is a ' + val
print "#{key} is a #{val}"
# Person = class: /name, /age, /sex.
@@ -54,7 +54,7 @@ for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
class Person
print: ->
print 'My name is ' + @name + '.'
print "My name is #{@name}."
# p = Person ()
@@ -74,7 +74,7 @@ class Policeman extends Person
(@rank) ->
print: ->
print 'My name is ' + @name + " and I'm a " + @rank + '.'
print "My name is #{@name} and I'm a #{@rank}."
print new Policeman 'Constable'
@@ -180,7 +180,7 @@ if 3.gender?
# session = url query ? at ('session').
HomePage::get = (url) ->
session = url.query.session if url.query?
session = url.query?.session
# BTree = class: /left, /right.

View File

@@ -7,6 +7,6 @@ server = http.createServer (req, res) ->
res.write 'Hello, World!'
res.end()
server.listen 3000
server.listen PORT = 3000
console.log "Server running at http://localhost:3000/"
console.log "Server running at http://localhost:#{PORT}/"

File diff suppressed because one or more lines are too long

1294
index.html

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var CoffeeScript, compile, runScripts,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
@@ -41,15 +41,18 @@
options.sourceMap = true;
options.inline = true;
_ref = CoffeeScript.compile(code, options), js = _ref.js, v3SourceMap = _ref.v3SourceMap;
return "" + js + "\n//@ sourceMappingURL=data:application/json;base64," + (btoa(unescape(encodeURIComponent(v3SourceMap)))) + "\n//@ sourceURL=coffeescript";
return "" + js + "\n//# sourceMappingURL=data:application/json;base64," + (btoa(unescape(encodeURIComponent(v3SourceMap)))) + "\n//# sourceURL=coffeescript";
};
}
CoffeeScript.load = function(url, callback, options) {
CoffeeScript.load = function(url, callback, options, hold) {
var xhr;
if (options == null) {
options = {};
}
if (hold == null) {
hold = false;
}
options.sourceFiles = [url];
xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') : new window.XMLHttpRequest();
xhr.open('GET', url, true);
@@ -57,15 +60,18 @@
xhr.overrideMimeType('text/plain');
}
xhr.onreadystatechange = function() {
var _ref;
var param, _ref;
if (xhr.readyState === 4) {
if ((_ref = xhr.status) === 0 || _ref === 200) {
CoffeeScript.run(xhr.responseText, options);
param = [xhr.responseText, options];
if (!hold) {
CoffeeScript.run.apply(CoffeeScript, param);
}
} else {
throw new Error("Could not load " + url);
}
if (callback) {
return callback();
return callback(param);
}
}
};
@@ -73,7 +79,7 @@
};
runScripts = function() {
var coffees, coffeetypes, execute, index, length, s, scripts;
var coffees, coffeetypes, execute, i, index, s, script, scripts, _fn, _i, _len;
scripts = window.document.getElementsByTagName('script');
coffeetypes = ['text/coffeescript', 'text/literate-coffeescript'];
coffees = (function() {
@@ -88,25 +94,35 @@
return _results;
})();
index = 0;
length = coffees.length;
(execute = function() {
var mediatype, options, script;
script = coffees[index++];
mediatype = script != null ? script.type : void 0;
if (__indexOf.call(coffeetypes, mediatype) >= 0) {
options = {
literate: mediatype === 'text/literate-coffeescript'
};
if (script.src) {
return CoffeeScript.load(script.src, execute, options);
} else {
options.sourceFiles = ['embedded'];
CoffeeScript.run(script.innerHTML, options);
return execute();
}
execute = function() {
var param;
param = coffees[index];
if (param instanceof Array) {
CoffeeScript.run.apply(CoffeeScript, param);
index++;
return execute();
}
})();
return null;
};
_fn = function(script, i) {
var options;
options = {
literate: script.type === coffeetypes[1]
};
if (script.src) {
return CoffeeScript.load(script.src, function(param) {
coffees[i] = param;
return execute();
}, options, true);
} else {
options.sourceFiles = ['embedded'];
return coffees[i] = [script.innerHTML, options];
}
};
for (i = _i = 0, _len = coffees.length; _i < _len; i = ++_i) {
script = coffees[i];
_fn(script, i);
}
return execute();
};
if (window.addEventListener) {

View File

@@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var CoffeeScript, cakefileDirectory, existsSync, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
fs = require('fs');
@@ -12,8 +12,6 @@
CoffeeScript = require('./coffee-script');
existsSync = fs.existsSync || path.existsSync;
tasks = {};
options = {};
@@ -101,7 +99,7 @@
cakefileDirectory = function(dir) {
var parent;
if (existsSync(path.join(dir, 'Cakefile'))) {
if (fs.existsSync(path.join(dir, 'Cakefile'))) {
return dir;
}
parent = path.normalize(path.join(dir, '..'));

View File

@@ -1,7 +1,8 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var Lexer, Module, SourceMap, child_process, compile, ext, findExtension, fork, formatSourcePosition, fs, helpers, lexer, loadFile, parser, patchStackTrace, patched, path, sourceMaps, vm, _i, _len, _ref,
__hasProp = {}.hasOwnProperty;
var Lexer, SourceMap, compile, formatSourcePosition, fs, getSourceMap, helpers, lexer, parser, path, sourceMaps, vm, withPrettyErrors,
__hasProp = {}.hasOwnProperty,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
fs = require('fs');
@@ -9,8 +10,6 @@
path = require('path');
child_process = require('child_process');
Lexer = require('./lexer').Lexer;
parser = require('./parser').parser;
@@ -19,16 +18,31 @@
SourceMap = require('./sourcemap');
exports.VERSION = '1.6.3';
exports.VERSION = '1.7.0';
exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md'];
exports.helpers = helpers;
exports.compile = compile = function(code, options) {
var answer, currentColumn, currentLine, fragment, fragments, header, js, map, merge, newLines, _i, _len;
if (options == null) {
options = {};
}
merge = helpers.merge;
withPrettyErrors = function(fn) {
return function(code, options) {
var err;
if (options == null) {
options = {};
}
try {
return fn.call(this, code, options);
} catch (_error) {
err = _error;
throw helpers.updateSyntaxError(err, code, options.filename);
}
};
};
exports.compile = compile = withPrettyErrors(function(code, options) {
var answer, currentColumn, currentLine, extend, fragment, fragments, header, js, map, merge, newLines, _i, _len;
merge = helpers.merge, extend = helpers.extend;
options = extend({}, options);
if (options.sourceMap) {
map = new SourceMap;
}
@@ -52,7 +66,11 @@
}
newLines = helpers.count(fragment.code, "\n");
currentLine += newLines;
currentColumn = fragment.code.length - (newLines ? fragment.code.lastIndexOf("\n") : 0);
if (newLines) {
currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1);
} else {
currentColumn += fragment.code.length;
}
}
js += fragment.code;
}
@@ -70,40 +88,35 @@
} else {
return js;
}
};
});
exports.tokens = function(code, options) {
exports.tokens = withPrettyErrors(function(code, options) {
return lexer.tokenize(code, options);
};
});
exports.nodes = function(source, options) {
exports.nodes = withPrettyErrors(function(source, options) {
if (typeof source === 'string') {
return parser.parse(lexer.tokenize(source, options));
} else {
return parser.parse(source);
}
};
});
exports.run = function(code, options) {
var answer, mainModule;
var answer, dir, mainModule, _ref;
if (options == null) {
options = {};
}
mainModule = require.main;
if (options.sourceMap == null) {
options.sourceMap = true;
}
mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.';
mainModule.moduleCache && (mainModule.moduleCache = {});
mainModule.paths = require('module')._nodeModulePaths(path.dirname(fs.realpathSync(options.filename || '.')));
dir = options.fileName ? path.dirname(fs.realpathSync(options.filename)) : fs.realpathSync('.');
mainModule.paths = require('module')._nodeModulePaths(dir);
if (!helpers.isCoffee(mainModule.filename) || require.extensions) {
answer = compile(code, options);
patchStackTrace();
sourceMaps[mainModule.filename] = answer.sourceMap;
return mainModule._compile(answer.js, mainModule.filename);
} else {
return mainModule._compile(code, mainModule.filename);
code = (_ref = answer.js) != null ? _ref : answer;
}
return mainModule._compile(code, mainModule.filename);
};
exports["eval"] = function(code, options) {
@@ -169,69 +182,29 @@
}
};
loadFile = function(module, filename) {
var answer, raw, stripped;
raw = fs.readFileSync(filename, 'utf8');
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
answer = compile(stripped, {
filename: filename,
sourceMap: true,
literate: helpers.isLiterate(filename)
});
sourceMaps[filename] = answer.sourceMap;
return module._compile(answer.js, filename);
exports.register = function() {
return require('./register');
};
if (require.extensions) {
_ref = ['.coffee', '.litcoffee', '.coffee.md'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
ext = _ref[_i];
require.extensions[ext] = loadFile;
exports._compileFile = function(filename, sourceMap) {
var answer, err, raw, stripped;
if (sourceMap == null) {
sourceMap = false;
}
Module = require('module');
findExtension = function(filename) {
var curExtension, extensions;
extensions = path.basename(filename).split('.');
if (extensions[0] === '') {
extensions.shift();
}
while (extensions.shift()) {
curExtension = '.' + extensions.join('.');
if (Module._extensions[curExtension]) {
return curExtension;
}
}
return '.js';
};
Module.prototype.load = function(filename) {
var extension;
this.filename = filename;
this.paths = Module._nodeModulePaths(path.dirname(filename));
extension = findExtension(filename);
Module._extensions[extension](this, filename);
return this.loaded = true;
};
}
if (child_process) {
fork = child_process.fork;
child_process.fork = function(path, args, options) {
var execPath;
if (args == null) {
args = [];
}
if (options == null) {
options = {};
}
execPath = helpers.isCoffee(path) ? 'coffee' : null;
if (!Array.isArray(args)) {
args = [];
options = args || {};
}
options.execPath || (options.execPath = execPath);
return fork(path, args, options);
};
}
raw = fs.readFileSync(filename, 'utf8');
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
try {
answer = compile(stripped, {
filename: filename,
sourceMap: sourceMap,
literate: helpers.isLiterate(filename)
});
} catch (_error) {
err = _error;
throw helpers.updateSyntaxError(err, stripped, filename);
}
return answer;
};
lexer = new Lexer;
@@ -241,6 +214,7 @@
token = this.tokens[this.pos++];
if (token) {
tag = token[0], this.yytext = token[1], this.yylloc = token[2];
this.errorToken = token.origin || token;
this.yylineno = this.yylloc.first_line;
} else {
tag = '';
@@ -259,52 +233,12 @@
parser.yy = require('./nodes');
parser.yy.parseError = function(message, _arg) {
var token;
var errorLoc, errorTag, errorText, errorToken, token, tokens, _ref;
token = _arg.token;
message = "unexpected " + (token === 1 ? 'end of input' : token);
return helpers.throwSyntaxError(message, parser.lexer.yylloc);
};
patched = false;
sourceMaps = {};
patchStackTrace = function() {
var mainModule;
if (patched) {
return;
}
patched = true;
mainModule = require.main;
return Error.prepareStackTrace = function(err, stack) {
var frame, frames, getSourceMapping, sourceFiles, _ref1;
sourceFiles = {};
getSourceMapping = function(filename, line, column) {
var answer, sourceMap;
sourceMap = sourceMaps[filename];
if (sourceMap) {
answer = sourceMap.sourceLocation([line - 1, column - 1]);
}
if (answer) {
return [answer[0] + 1, answer[1] + 1];
} else {
return null;
}
};
frames = (function() {
var _j, _len1, _results;
_results = [];
for (_j = 0, _len1 = stack.length; _j < _len1; _j++) {
frame = stack[_j];
if (frame.getFunction() === exports.run) {
break;
}
_results.push(" at " + (formatSourcePosition(frame, getSourceMapping)));
}
return _results;
})();
return "" + err.name + ": " + ((_ref1 = err.message) != null ? _ref1 : '') + "\n" + (frames.join('\n')) + "\n";
};
_ref = parser.lexer, errorToken = _ref.errorToken, tokens = _ref.tokens;
errorTag = errorToken[0], errorText = errorToken[1], errorLoc = errorToken[2];
errorText = errorToken === tokens[tokens.length - 1] ? 'end of input' : errorTag === 'INDENT' || errorTag === 'OUTDENT' ? 'indentation' : helpers.nameWhitespaceCharacter(errorText);
return helpers.throwSyntaxError("unexpected " + errorText, errorLoc);
};
formatSourcePosition = function(frame, getSourceMapping) {
@@ -326,7 +260,7 @@
line = frame.getLineNumber();
column = frame.getColumnNumber();
source = getSourceMapping(fileName, line, column);
fileLocation = source ? "" + fileName + ":" + source[0] + ":" + source[1] + ", <js>:" + line + ":" + column : "" + fileName + ":" + line + ":" + column;
fileLocation = source ? "" + fileName + ":" + source[0] + ":" + source[1] : "" + fileName + ":" + line + ":" + column;
}
functionName = frame.getFunctionName();
isConstructor = frame.isConstructor();
@@ -355,4 +289,47 @@
}
};
sourceMaps = {};
getSourceMap = function(filename) {
var answer, _ref;
if (sourceMaps[filename]) {
return sourceMaps[filename];
}
if (_ref = path != null ? path.extname(filename) : void 0, __indexOf.call(exports.FILE_EXTENSIONS, _ref) < 0) {
return;
}
answer = exports._compileFile(filename, true);
return sourceMaps[filename] = answer.sourceMap;
};
Error.prepareStackTrace = function(err, stack) {
var frame, frames, getSourceMapping, _ref;
getSourceMapping = function(filename, line, column) {
var answer, sourceMap;
sourceMap = getSourceMap(filename);
if (sourceMap) {
answer = sourceMap.sourceLocation([line - 1, column - 1]);
}
if (answer) {
return [answer[0] + 1, answer[1] + 1];
} else {
return null;
}
};
frames = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = stack.length; _i < _len; _i++) {
frame = stack[_i];
if (frame.getFunction() === exports.run) {
break;
}
_results.push(" at " + (formatSourcePosition(frame, getSourceMapping)));
}
return _results;
})();
return "" + err.name + ": " + ((_ref = err.message) != null ? _ref : '') + "\n" + (frames.join('\n')) + "\n";
};
}).call(this);

View File

@@ -1,6 +1,7 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, exists, forkNode, fs, helpers, hidden, joinTimeout, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, useWinPathSep, version, wait, watch, watchDir, watchers, writeJs, _ref;
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, _ref,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
fs = require('fs');
@@ -12,12 +13,12 @@
CoffeeScript = require('./coffee-script');
mkdirp = require('mkdirp');
_ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
EventEmitter = require('events').EventEmitter;
exists = fs.exists || path.exists;
useWinPathSep = path.sep === '\\';
helpers.extend(CoffeeScript, new EventEmitter);
@@ -36,7 +37,7 @@
BANNER = 'Usage: coffee [options] path/to/script.coffee -- [args]\n\nIf called without options, `coffee` will run your script.';
SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-m', '--map', 'generate source map and save as .map files'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-l', '--literate', 'treat stdio as literate style coffee-script'], ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']];
SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-m', '--map', 'generate source map and save as .map files'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['--no-header', 'suppress the "Generated by" header'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-l', '--literate', 'treat stdio as literate style coffee-script'], ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']];
opts = {};
@@ -46,13 +47,16 @@
notSources = {};
watchers = {};
watchedDirs = {};
optionParser = null;
exports.run = function() {
var literals, source, _i, _len, _results;
var literals, replCliOpts, source, _i, _len, _ref1, _results;
parseOptions();
replCliOpts = {
useGlobal: true
};
if (opts.nodejs) {
return forkNode();
}
@@ -63,94 +67,125 @@
return version();
}
if (opts.interactive) {
return require('./repl').start();
}
if (opts.watch && !fs.watch) {
return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + ".");
return require('./repl').start(replCliOpts);
}
if (opts.stdio) {
return compileStdio();
}
if (opts["eval"]) {
return compileScript(null, sources[0]);
return compileScript(null, opts["arguments"][0]);
}
if (!sources.length) {
return require('./repl').start();
if (!opts["arguments"].length) {
return require('./repl').start(replCliOpts);
}
literals = opts.run ? sources.splice(1) : [];
literals = opts.run ? opts["arguments"].splice(1) : [];
process.argv = process.argv.slice(0, 2).concat(literals);
process.argv[0] = 'coffee';
if (opts.output) {
opts.output = path.resolve(opts.output);
}
if (opts.join) {
opts.join = path.resolve(opts.join);
}
_ref1 = opts["arguments"];
_results = [];
for (_i = 0, _len = sources.length; _i < _len; _i++) {
source = sources[_i];
_results.push(compilePath(source, true, path.normalize(source)));
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
source = _ref1[_i];
source = path.resolve(source);
_results.push(compilePath(source, true, source));
}
return _results;
};
compilePath = function(source, topLevel, base) {
return fs.stat(source, function(err, stats) {
if (err && err.code !== 'ENOENT') {
throw err;
}
if ((err != null ? err.code : void 0) === 'ENOENT') {
var code, err, file, files, stats, _i, _len, _results;
if (__indexOf.call(sources, source) >= 0 || watchedDirs[source] || !topLevel && (notSources[source] || hidden(source))) {
return;
}
try {
stats = fs.statSync(source);
} catch (_error) {
err = _error;
if (err.code === 'ENOENT') {
console.error("File not found: " + source);
process.exit(1);
}
if (stats.isDirectory() && path.dirname(source) !== 'node_modules') {
if (opts.watch) {
watchDir(source, base);
}
return fs.readdir(source, function(err, files) {
var file, index, _ref1, _ref2;
if (err && err.code !== 'ENOENT') {
throw err;
}
if ((err != null ? err.code : void 0) === 'ENOENT') {
return;
}
index = sources.indexOf(source);
files = files.filter(function(file) {
return !hidden(file);
});
[].splice.apply(sources, [index, index - index + 1].concat(_ref1 = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(path.join(source, file));
}
return _results;
})())), _ref1;
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() {
return null;
}))), _ref2;
return files.forEach(function(file) {
return compilePath(path.join(source, file), false, base);
});
});
} else if (topLevel || helpers.isCoffee(source)) {
if (opts.watch) {
watch(source, base);
}
return fs.readFile(source, function(err, code) {
if (err && err.code !== 'ENOENT') {
throw err;
}
if ((err != null ? err.code : void 0) === 'ENOENT') {
return;
}
return compileScript(source, code.toString(), base);
});
} else {
throw err;
}
if (stats.isDirectory()) {
if (path.basename(source) === 'node_modules') {
notSources[source] = true;
return removeSource(source, base);
return;
}
});
if (opts.run) {
compilePath(findDirectoryIndex(source), topLevel, base);
return;
}
if (opts.watch) {
watchDir(source, base);
}
try {
files = fs.readdirSync(source);
} catch (_error) {
err = _error;
if (err.code === 'ENOENT') {
return;
} else {
throw err;
}
}
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(compilePath(path.join(source, file), false, base));
}
return _results;
} else if (topLevel || helpers.isCoffee(source)) {
sources.push(source);
sourceCode.push(null);
delete notSources[source];
if (opts.watch) {
watch(source, base);
}
try {
code = fs.readFileSync(source);
} catch (_error) {
err = _error;
if (err.code === 'ENOENT') {
return;
} else {
throw err;
}
}
return compileScript(source, code.toString(), base);
} else {
return notSources[source] = true;
}
};
findDirectoryIndex = function(source) {
var err, ext, index, _i, _len, _ref1;
_ref1 = CoffeeScript.FILE_EXTENSIONS;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
ext = _ref1[_i];
index = path.join(source, "index" + ext);
try {
if ((fs.statSync(index)).isFile()) {
return index;
}
} catch (_error) {
err = _error;
if (err.code !== 'ENOENT') {
throw err;
}
}
}
console.error("Missing index.coffee or index.litcoffee in " + source);
return process.exit(1);
};
compileScript = function(file, input, base) {
var compiled, err, message, o, options, t, task, useColors;
var compiled, err, message, o, options, t, task;
if (base == null) {
base = null;
}
@@ -168,6 +203,7 @@
} else if (o.nodes) {
return printLine(CoffeeScript.nodes(t.input, t.options).toString().trim());
} else if (o.run) {
CoffeeScript.register();
return CoffeeScript.run(t.input, t.options);
} else if (o.join && t.file !== o.join) {
if (helpers.isLiterate(file)) {
@@ -195,8 +231,7 @@
if (CoffeeScript.listeners('failure').length) {
return;
}
useColors = process.stdout.isTTY && !process.env.NODE_DISABLE_COLORS;
message = helpers.prettyErrorMessage(err, file || '[stdin]', input, useColors);
message = err.stack || ("" + err);
if (o.watch) {
return printLine(message + '\x07');
} else {
@@ -237,24 +272,23 @@
};
watch = function(source, base) {
var compile, compileTimeout, e, prevStats, rewatch, watchErr, watcher;
var compile, compileTimeout, err, prevStats, rewatch, startWatcher, watchErr, watcher;
watcher = null;
prevStats = null;
compileTimeout = null;
watchErr = function(e) {
if (e.code === 'ENOENT') {
if (sources.indexOf(source) === -1) {
return;
}
try {
rewatch();
return compile();
} catch (_error) {
e = _error;
removeSource(source, base, true);
return compileJoin();
}
} else {
throw e;
watchErr = function(err) {
if (err.code !== 'ENOENT') {
throw err;
}
if (__indexOf.call(sources, source) < 0) {
return;
}
try {
rewatch();
return compile();
} catch (_error) {
removeSource(source, base);
return compileJoin();
}
};
compile = function() {
@@ -278,119 +312,130 @@
});
});
};
try {
watcher = fs.watch(source, compile);
} catch (_error) {
e = _error;
watchErr(e);
}
return rewatch = function() {
startWatcher = function() {
return watcher = fs.watch(source).on('change', compile).on('error', function(err) {
if (err.code !== 'EPERM') {
throw err;
}
return removeSource(source, base);
});
};
rewatch = function() {
if (watcher != null) {
watcher.close();
}
return watcher = fs.watch(source, compile);
return startWatcher();
};
try {
return startWatcher();
} catch (_error) {
err = _error;
return watchErr(err);
}
};
watchDir = function(source, base) {
var e, readdirTimeout, watcher;
var err, readdirTimeout, startWatcher, stopWatcher, watcher;
watcher = null;
readdirTimeout = null;
try {
return watcher = fs.watch(source, function() {
startWatcher = function() {
return watcher = fs.watch(source).on('error', function(err) {
if (err.code !== 'EPERM') {
throw err;
}
return stopWatcher();
}).on('change', function() {
clearTimeout(readdirTimeout);
return readdirTimeout = wait(25, function() {
return fs.readdir(source, function(err, files) {
var file, _i, _len, _results;
if (err) {
if (err.code !== 'ENOENT') {
throw err;
}
watcher.close();
return unwatchDir(source, base);
var err, file, files, _i, _len, _results;
try {
files = fs.readdirSync(source);
} catch (_error) {
err = _error;
if (err.code !== 'ENOENT') {
throw err;
}
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
if (!(!hidden(file) && !notSources[file])) {
continue;
}
file = path.join(source, file);
if (sources.some(function(s) {
return s.indexOf(file) >= 0;
})) {
continue;
}
sources.push(file);
sourceCode.push(null);
_results.push(compilePath(file, false, base));
}
return _results;
});
return stopWatcher();
}
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(compilePath(path.join(source, file), false, base));
}
return _results;
});
});
};
stopWatcher = function() {
watcher.close();
return removeSourceDir(source, base);
};
watchedDirs[source] = true;
try {
return startWatcher();
} catch (_error) {
e = _error;
if (e.code !== 'ENOENT') {
throw e;
err = _error;
if (err.code !== 'ENOENT') {
throw err;
}
}
};
unwatchDir = function(source, base) {
var file, prevSources, toRemove, _i, _len;
prevSources = sources.slice(0);
toRemove = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = sources.length; _i < _len; _i++) {
file = sources[_i];
if (file.indexOf(source) >= 0) {
_results.push(file);
}
removeSourceDir = function(source, base) {
var file, sourcesChanged, _i, _len;
delete watchedDirs[source];
sourcesChanged = false;
for (_i = 0, _len = sources.length; _i < _len; _i++) {
file = sources[_i];
if (!(source === path.dirname(file))) {
continue;
}
return _results;
})();
for (_i = 0, _len = toRemove.length; _i < _len; _i++) {
file = toRemove[_i];
removeSource(file, base, true);
removeSource(file, base);
sourcesChanged = true;
}
if (!sources.some(function(s, i) {
return prevSources[i] !== s;
})) {
return;
if (sourcesChanged) {
return compileJoin();
}
return compileJoin();
};
removeSource = function(source, base, removeJs) {
var index, jsPath;
removeSource = function(source, base) {
var index;
index = sources.indexOf(source);
sources.splice(index, 1);
sourceCode.splice(index, 1);
if (removeJs && !opts.join) {
jsPath = outputPath(source, base);
return exists(jsPath, function(itExists) {
if (itExists) {
return fs.unlink(jsPath, function(err) {
if (err && err.code !== 'ENOENT') {
throw err;
}
return timeLog("removed " + source);
});
}
});
if (!opts.join) {
silentUnlink(outputPath(source, base));
silentUnlink(outputPath(source, base, '.map'));
return timeLog("removed " + source);
}
};
silentUnlink = function(path) {
var err, _ref1;
try {
return fs.unlinkSync(path);
} catch (_error) {
err = _error;
if ((_ref1 = err.code) !== 'ENOENT' && _ref1 !== 'EPERM') {
throw err;
}
}
};
outputPath = function(source, base, extension) {
var baseDir, basename, dir, srcDir;
var basename, dir, srcDir;
if (extension == null) {
extension = ".js";
}
basename = helpers.baseFileName(source, true, useWinPathSep);
srcDir = path.dirname(source);
baseDir = base === '.' ? srcDir : srcDir.substring(base.length);
dir = opts.output ? path.join(opts.output, baseDir) : srcDir;
if (!opts.output) {
dir = srcDir;
} else if (source === base) {
dir = opts.output;
} else {
dir = path.join(opts.output, path.relative(base, srcDir));
}
return path.join(dir, basename + extension);
};
@@ -407,7 +452,7 @@
js = ' ';
}
if (generatedSourceMap) {
js = "" + js + "\n/*\n//@ sourceMappingURL=" + (helpers.baseFileName(sourceMapPath, false, useWinPathSep)) + "\n*/\n";
js = "" + js + "\n//# sourceMappingURL=" + (helpers.baseFileName(sourceMapPath, false, useWinPathSep)) + "\n";
}
fs.writeFile(jsPath, js, function(err) {
if (err) {
@@ -425,11 +470,11 @@
});
}
};
return exists(jsDir, function(itExists) {
return fs.exists(jsDir, function(itExists) {
if (itExists) {
return compile();
} else {
return exec("mkdir -p " + jsDir, compile);
return mkdirp(jsDir, compile);
}
});
};
@@ -459,17 +504,12 @@
};
parseOptions = function() {
var i, o, source, _i, _len;
var o;
optionParser = new optparse.OptionParser(SWITCHES, BANNER);
o = opts = optionParser.parse(process.argv.slice(2));
o.compile || (o.compile = !!o.output);
o.run = !(o.compile || o.print || o.map);
o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
sources = o["arguments"];
for (i = _i = 0, _len = sources.length; _i < _len; i = ++_i) {
source = sources[i];
sourceCode[i] = null;
}
return o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
};
compileOptions = function(filename, base) {
@@ -478,7 +518,7 @@
filename: filename,
literate: opts.literate || helpers.isLiterate(filename),
bare: opts.bare,
header: opts.compile,
header: opts.compile && !opts['no-header'],
sourceMap: opts.map
};
if (filename) {
@@ -504,15 +544,18 @@
};
forkNode = function() {
var args, nodeArgs;
var args, nodeArgs, p;
nodeArgs = opts.nodejs.split(/\s+/);
args = process.argv.slice(1);
args.splice(args.indexOf('--nodejs'), 2);
return spawn(process.execPath, nodeArgs.concat(args), {
p = spawn(process.execPath, nodeArgs.concat(args), {
cwd: process.cwd(),
env: process.env,
customFds: [0, 1, 2]
});
return p.on('exit', function(code) {
return process.exit(code);
});
};
usage = function() {

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
@@ -32,7 +32,7 @@
Root: [
o('', function() {
return new Block;
}), o('Body'), o('Block TERMINATOR')
}), o('Body')
],
Body: [
o('Line', function() {
@@ -96,8 +96,7 @@
return new Value($1);
}), o('ObjAssignable : Expression', function() {
return new Assign(LOC(1)(new Value($1)), $3, 'object');
}), o('ObjAssignable :\
INDENT Expression OUTDENT', function() {
}), o('ObjAssignable : INDENT Expression OUTDENT', function() {
return new Assign(LOC(1)(new Value($1)), $4, 'object');
}), o('Comment')
],
@@ -149,6 +148,8 @@
return new Param($1, null, true);
}), o('ParamVar = Expression', function() {
return new Param($1, $3);
}), o('...', function() {
return new Expansion;
})
],
ParamVar: [o('Identifier'), o('ThisProperty'), o('Array'), o('Object')],
@@ -328,7 +329,11 @@
return $1.concat($4);
})
],
Arg: [o('Expression'), o('Splat')],
Arg: [
o('Expression'), o('Splat'), o('...', function() {
return new Expansion;
})
],
SimpleArgs: [
o('Expression'), o('SimpleArgs , Expression', function() {
return [].concat($1, $3);
@@ -514,9 +519,9 @@
type: $1
});
}), o('IfBlock ELSE IF Expression Block', function() {
return $1.addElse(new If($4, $5, {
return $1.addElse(LOC(3, 5)(new If($4, $5, {
type: $3
}));
})));
})
],
If: [
@@ -537,14 +542,16 @@
Operation: [
o('UNARY Expression', function() {
return new Op($1, $2);
}), o('UNARY_MATH Expression', function() {
return new Op($1, $2);
}), o('- Expression', (function() {
return new Op('-', $2);
}), {
prec: 'UNARY'
prec: 'UNARY_MATH'
}), o('+ Expression', (function() {
return new Op('+', $2);
}), {
prec: 'UNARY'
prec: 'UNARY_MATH'
}), o('-- SimpleAssignable', function() {
return new Op('--', $2);
}), o('++ SimpleAssignable', function() {
@@ -561,6 +568,8 @@
return new Op('-', $1, $3);
}), o('Expression MATH Expression', function() {
return new Op($2, $1, $3);
}), o('Expression ** Expression', function() {
return new Op($2, $1, $3);
}), o('Expression SHIFT Expression', function() {
return new Op($2, $1, $3);
}), o('Expression COMPARE Expression', function() {
@@ -573,14 +582,11 @@
} else {
return new Op($2, $1, $3);
}
}), o('SimpleAssignable COMPOUND_ASSIGN\
Expression', function() {
}), o('SimpleAssignable COMPOUND_ASSIGN Expression', function() {
return new Assign($1, $3, $2);
}), o('SimpleAssignable COMPOUND_ASSIGN\
INDENT Expression OUTDENT', function() {
}), o('SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT', function() {
return new Assign($1, $4, $2);
}), o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR\
Expression', function() {
}), o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR Expression', function() {
return new Assign($1, $4, $2);
}), o('SimpleAssignable EXTENDS Expression', function() {
return new Extends($1, $3);
@@ -588,7 +594,7 @@
]
};
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF']];
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['right', '**'], ['right', 'UNARY_MATH'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['left', 'POST_IF']];
tokens = [];

View File

@@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var buildLocationData, extend, flatten, last, repeat, _ref;
var buildLocationData, extend, flatten, last, repeat, syntaxErrorToString, _ref;
exports.starts = function(string, literal, start) {
return literal === string.substr(start, literal.length);
@@ -167,7 +167,7 @@
pathSep = useWinPathSep ? /\\|\// : /\//;
parts = file.split(pathSep);
file = parts[parts.length - 1];
if (!stripExt) {
if (!(stripExt && file.indexOf('.') >= 0)) {
return file;
}
parts = file.split('.');
@@ -188,36 +188,65 @@
exports.throwSyntaxError = function(message, location) {
var error;
if (location.last_line == null) {
location.last_line = location.first_line;
}
if (location.last_column == null) {
location.last_column = location.first_column;
}
error = new SyntaxError(message);
error.location = location;
error.toString = syntaxErrorToString;
error.stack = error.toString();
throw error;
};
exports.prettyErrorMessage = function(error, fileName, code, useColors) {
var codeLine, colorize, end, first_column, first_line, last_column, last_line, marker, message, start, _ref1;
if (!error.location) {
return error.stack || ("" + error);
exports.updateSyntaxError = function(error, code, filename) {
if (error.toString === syntaxErrorToString) {
error.code || (error.code = code);
error.filename || (error.filename = filename);
error.stack = error.toString();
}
_ref1 = error.location, first_line = _ref1.first_line, first_column = _ref1.first_column, last_line = _ref1.last_line, last_column = _ref1.last_column;
codeLine = code.split('\n')[first_line];
return error;
};
syntaxErrorToString = function() {
var codeLine, colorize, colorsEnabled, end, filename, first_column, first_line, last_column, last_line, marker, start, _ref1, _ref2;
if (!(this.code && this.location)) {
return Error.prototype.toString.call(this);
}
_ref1 = this.location, first_line = _ref1.first_line, first_column = _ref1.first_column, last_line = _ref1.last_line, last_column = _ref1.last_column;
if (last_line == null) {
last_line = first_line;
}
if (last_column == null) {
last_column = first_column;
}
filename = this.filename || '[stdin]';
codeLine = this.code.split('\n')[first_line];
start = first_column;
end = first_line === last_line ? last_column + 1 : codeLine.length;
marker = repeat(' ', start) + repeat('^', end - start);
if (useColors) {
if (typeof process !== "undefined" && process !== null) {
colorsEnabled = process.stdout.isTTY && !process.env.NODE_DISABLE_COLORS;
}
if ((_ref2 = this.colorful) != null ? _ref2 : colorsEnabled) {
colorize = function(str) {
return "\x1B[1;31m" + str + "\x1B[0m";
};
codeLine = codeLine.slice(0, start) + colorize(codeLine.slice(start, end)) + codeLine.slice(end);
marker = colorize(marker);
}
message = "" + fileName + ":" + (first_line + 1) + ":" + (first_column + 1) + ": error: " + error.message + "\n" + codeLine + "\n" + marker;
return message;
return "" + filename + ":" + (first_line + 1) + ":" + (first_column + 1) + ": error: " + this.message + "\n" + codeLine + "\n" + marker;
};
exports.nameWhitespaceCharacter = function(string) {
switch (string) {
case ' ':
return 'space';
case '\n':
return 'newline';
case '\r':
return 'carriage return';
case '\t':
return 'tab';
default:
return string;
}
};
}).call(this);

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var key, val, _ref;

View File

@@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1,
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, UNARY_MATH, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
_ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES;
@@ -17,6 +17,7 @@
}
this.literate = opts.literate;
this.indent = 0;
this.baseIndent = 0;
this.indebt = 0;
this.outdebt = 0;
this.indents = [];
@@ -165,30 +166,25 @@
};
Lexer.prototype.stringToken = function() {
var match, octalEsc, string;
switch (this.chunk.charAt(0)) {
var octalEsc, quote, string, trimmed;
switch (quote = this.chunk.charAt(0)) {
case "'":
if (!(match = SIMPLESTR.exec(this.chunk))) {
return 0;
}
string = match[0];
this.token('STRING', string.replace(MULTILINER, '\\\n'), 0, string.length);
string = SIMPLESTR.exec(this.chunk)[0];
break;
case '"':
if (!(string = this.balancedString(this.chunk, '"'))) {
return 0;
}
if (0 < string.indexOf('#{', 1)) {
this.interpolateString(string.slice(1, -1), {
strOffset: 1,
lexedLength: string.length
});
} else {
this.token('STRING', this.escapeLines(string, 0, string.length));
}
break;
default:
return 0;
string = this.balancedString(this.chunk, '"');
}
if (!string) {
return 0;
}
trimmed = this.removeNewlines(string.slice(1, -1));
if (quote === '"' && 0 < string.indexOf('#{', 1)) {
this.interpolateString(trimmed, {
strOffset: 1,
lexedLength: string.length
});
} else {
this.token('STRING', quote + this.escapeLines(trimmed) + quote, 0, string.length);
}
if (octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test(string)) {
this.error("octal escape sequences " + string + " are not allowed");
@@ -248,8 +244,7 @@
if (this.chunk.charAt(0) !== '/') {
return 0;
}
if (match = HEREGEX.exec(this.chunk)) {
length = this.heregexToken(match);
if (length = this.heregexToken()) {
return length;
}
prev = last(this.tokens);
@@ -260,21 +255,24 @@
return 0;
}
_ref3 = match, match = _ref3[0], regex = _ref3[1], flags = _ref3[2];
if (regex === '//') {
return 0;
}
if (regex.slice(0, 2) === '/*') {
this.error('regular expressions cannot begin with `*`');
}
if (regex === '//') {
regex = '/(?:)/';
}
this.token('REGEX', "" + regex + flags, 0, match.length);
return match.length;
};
Lexer.prototype.heregexToken = function(match) {
var body, flags, flagsOffset, heregex, plusToken, prev, re, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
Lexer.prototype.heregexToken = function() {
var body, flags, flagsOffset, heregex, match, plusToken, prev, re, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
if (!(match = HEREGEX.exec(this.chunk))) {
return 0;
}
heregex = match[0], body = match[1], flags = match[2];
if (0 > body.indexOf('#{')) {
re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/');
re = this.escapeLines(body.replace(HEREGEX_OMIT, '$1$2').replace(/\//g, '\\/'), true);
if (re.match(/^\*/)) {
this.error('regular expressions cannot begin with `*`');
}
@@ -293,7 +291,7 @@
if (tag === 'TOKENS') {
tokens.push.apply(tokens, value);
} else if (tag === 'NEOSTRING') {
if (!(value = value.replace(HEREGEX_OMIT, ''))) {
if (!(value = value.replace(HEREGEX_OMIT, '$1$2'))) {
continue;
}
value = value.replace(/\\/g, '\\\\');
@@ -346,37 +344,48 @@
this.suppressNewlines();
return indent.length;
}
if (!this.tokens.length) {
this.baseIndent = this.indent = size;
return indent.length;
}
diff = size - this.indent + this.outdebt;
this.token('INDENT', diff, indent.length - size, size);
this.indents.push(diff);
this.ends.push('OUTDENT');
this.outdebt = this.indebt = 0;
this.indent = size;
} else if (size < this.baseIndent) {
this.error('missing indentation', indent.length);
} else {
this.indebt = 0;
this.outdentToken(this.indent - size, noNewlines, indent.length);
}
this.indent = size;
return indent.length;
};
Lexer.prototype.outdentToken = function(moveOut, noNewlines, outdentLength) {
var dent, len;
var decreasedIndent, dent, lastIndent, _ref2;
decreasedIndent = this.indent - moveOut;
while (moveOut > 0) {
len = this.indents.length - 1;
if (this.indents[len] === void 0) {
lastIndent = this.indents[this.indents.length - 1];
if (!lastIndent) {
moveOut = 0;
} else if (this.indents[len] === this.outdebt) {
} else if (lastIndent === this.outdebt) {
moveOut -= this.outdebt;
this.outdebt = 0;
} else if (this.indents[len] < this.outdebt) {
this.outdebt -= this.indents[len];
moveOut -= this.indents[len];
} else if (lastIndent < this.outdebt) {
this.outdebt -= lastIndent;
moveOut -= lastIndent;
} else {
dent = this.indents.pop() + this.outdebt;
moveOut -= dent;
if (outdentLength && (_ref2 = this.chunk[outdentLength], __indexOf.call(INDENTABLE_CLOSERS, _ref2) >= 0)) {
decreasedIndent -= dent - moveOut;
moveOut = dent;
}
this.outdebt = 0;
this.pair('OUTDENT');
this.token('OUTDENT', dent, 0, outdentLength);
this.token('OUTDENT', moveOut, 0, outdentLength);
moveOut -= dent;
}
}
if (dent) {
@@ -388,6 +397,7 @@
if (!(this.tag() === 'TERMINATOR' || noNewlines)) {
this.token('TERMINATOR', '\n', outdentLength, 0);
}
this.indent = decreasedIndent;
return this;
};
@@ -457,6 +467,8 @@
tag = 'COMPOUND_ASSIGN';
} else if (__indexOf.call(UNARY, value) >= 0) {
tag = 'UNARY';
} else if (__indexOf.call(UNARY_MATH, value) >= 0) {
tag = 'UNARY_MATH';
} else if (__indexOf.call(SHIFT, value) >= 0) {
tag = 'SHIFT';
} else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) {
@@ -586,18 +598,14 @@
};
Lexer.prototype.interpolateString = function(str, options) {
var column, expr, heredoc, i, inner, interpolated, len, letter, lexedLength, line, locationToken, nested, offsetInChunk, pi, plusToken, popped, regex, rparen, strOffset, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
var column, errorToken, expr, heredoc, i, inner, interpolated, len, letter, lexedLength, line, locationToken, nested, offsetInChunk, pi, plusToken, popped, regex, rparen, strOffset, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
if (options == null) {
options = {};
}
heredoc = options.heredoc, regex = options.regex, offsetInChunk = options.offsetInChunk, strOffset = options.strOffset, lexedLength = options.lexedLength;
offsetInChunk = offsetInChunk || 0;
strOffset = strOffset || 0;
lexedLength = lexedLength || str.length;
if (heredoc && str.length > 0 && str[0] === '\n') {
str = str.slice(1);
strOffset++;
}
offsetInChunk || (offsetInChunk = 0);
strOffset || (strOffset = 0);
lexedLength || (lexedLength = str.length);
tokens = [];
pi = 0;
i = -1;
@@ -612,6 +620,9 @@
if (pi < i) {
tokens.push(this.makeToken('NEOSTRING', str.slice(pi, i), strOffset + pi));
}
if (!errorToken) {
errorToken = this.makeToken('', 'string interpolation', offsetInChunk + i + 1, 2);
}
inner = expr.slice(1, -1);
if (inner.length) {
_ref2 = this.getLineAndColumnFromChunk(strOffset + i + 1), line = _ref2[0], column = _ref2[1];
@@ -648,7 +659,7 @@
tokens.unshift(this.makeToken('NEOSTRING', '', offsetInChunk));
}
if (interpolated = tokens.length > 1) {
this.token('(', '(', offsetInChunk, 0);
this.token('(', '(', offsetInChunk, 0, errorToken);
}
for (i = _i = 0, _len = tokens.length; _i < _len; i = ++_i) {
token = tokens[i];
@@ -684,13 +695,12 @@
};
Lexer.prototype.pair = function(tag) {
var size, wanted;
var wanted;
if (tag !== (wanted = last(this.ends))) {
if ('OUTDENT' !== wanted) {
this.error("unmatched " + tag);
}
this.indent -= size = last(this.indents);
this.outdentToken(size, true);
this.outdentToken(last(this.indents), true);
return this.pair(tag);
}
return this.ends.pop();
@@ -733,9 +743,12 @@
return token;
};
Lexer.prototype.token = function(tag, value, offsetInChunk, length) {
Lexer.prototype.token = function(tag, value, offsetInChunk, length, origin) {
var token;
token = this.makeToken(tag, value, offsetInChunk, length);
if (origin) {
token.origin = origin;
}
this.tokens.push(token);
return token;
};
@@ -752,19 +765,34 @@
Lexer.prototype.unfinished = function() {
var _ref2;
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === 'UNARY_MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === '**' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
};
Lexer.prototype.removeNewlines = function(str) {
return str.replace(/^\s*\n\s*/, '').replace(/([^\\]|\\\\)\s*\n\s*$/, '$1');
};
Lexer.prototype.escapeLines = function(str, heredoc) {
return str.replace(MULTILINER, heredoc ? '\\n' : '');
str = str.replace(/\\[^\S\n]*(\n|\\)\s*/g, function(escaped, character) {
if (character === '\n') {
return '';
} else {
return escaped;
}
});
if (heredoc) {
return str.replace(MULTILINER, '\\n');
} else {
return str.replace(/\s*\n\s*/g, ' ');
}
};
Lexer.prototype.makeString = function(body, quote, heredoc) {
if (!body) {
return quote + quote;
}
body = body.replace(/\\([\s\S])/g, function(match, contents) {
if (contents === '\n' || contents === quote) {
body = body.replace(RegExp("\\\\(" + quote + "|\\\\)", "g"), function(match, contents) {
if (contents === quote) {
return contents;
} else {
return match;
@@ -774,10 +802,15 @@
return quote + this.escapeLines(body, heredoc) + quote;
};
Lexer.prototype.error = function(message) {
Lexer.prototype.error = function(message, offset) {
var first_column, first_line, _ref2;
if (offset == null) {
offset = 0;
}
_ref2 = this.getLineAndColumnFromChunk(offset), first_line = _ref2[0], first_column = _ref2[1];
return throwSyntaxError(message, {
first_line: this.chunkLine,
first_column: this.chunkColumn
first_line: first_line,
first_column: first_column
});
};
@@ -828,27 +861,27 @@
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
HEREDOC = /^("""|''')((?:\\[\s\S]|[^\\])*?)(?:\n[^\n\S]*)?\1/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?(\.|::)|\.{2,3})/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?(\.|::)|\.{2,3})/;
WHITESPACE = /^[^\n\S]+/;
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/;
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/;
CODE = /^[-=]>/;
MULTI_DENT = /^(?:\n[^\n\S]*)+/;
SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/;
SIMPLESTR = /^'[^\\']*(?:\\[\s\S][^\\']*)*'/;
JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/;
REGEX = /^(\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)([imgy]{0,4})(?!\w)/;
HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?!\w)/;
HEREGEX = /^\/{3}((?:\\?[\s\S])+?)\/{3}([imgy]{0,4})(?!\w)/;
HEREGEX_OMIT = /\s+(?:#.*)?/g;
HEREGEX_OMIT = /((?:\\\\)+)|\\(\s|\/)|\s+(?:#.*)?/g;
MULTILINER = /\n/g;
@@ -860,9 +893,11 @@
TRAILING_SPACES = /\s+$/;
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=', '**=', '//=', '%%='];
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO'];
UNARY = ['NEW', 'TYPEOF', 'DELETE', 'DO'];
UNARY_MATH = ['!', '~'];
LOGIC = ['&&', '||', '&', '|', '^'];
@@ -870,7 +905,7 @@
COMPARE = ['==', '!=', '<', '>', '<=', '>='];
MATH = ['*', '/', '%'];
MATH = ['*', '/', '%', '//', '%%'];
RELATION = ['IN', 'OF', 'INSTANCEOF'];
@@ -886,4 +921,6 @@
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
INDENTABLE_CLOSERS = [')', '}', ']'];
}).call(this);

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,66 @@
// Generated by CoffeeScript 1.7.0
(function() {
var CoffeeScript, Module, binary, child_process, ext, findExtension, fork, helpers, loadFile, path, _i, _len, _ref;
CoffeeScript = require('./coffee-script');
child_process = require('child_process');
helpers = require('./helpers');
path = require('path');
loadFile = function(module, filename) {
var answer;
answer = CoffeeScript._compileFile(filename, false);
return module._compile(answer, filename);
};
if (require.extensions) {
_ref = CoffeeScript.FILE_EXTENSIONS;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
ext = _ref[_i];
require.extensions[ext] = loadFile;
}
Module = require('module');
findExtension = function(filename) {
var curExtension, extensions;
extensions = path.basename(filename).split('.');
if (extensions[0] === '') {
extensions.shift();
}
while (extensions.shift()) {
curExtension = '.' + extensions.join('.');
if (Module._extensions[curExtension]) {
return curExtension;
}
}
return '.js';
};
Module.prototype.load = function(filename) {
var extension;
this.filename = filename;
this.paths = Module._nodeModulePaths(path.dirname(filename));
extension = findExtension(filename);
Module._extensions[extension](this, filename);
return this.loaded = true;
};
}
if (child_process) {
fork = child_process.fork;
binary = require.resolve('../../bin/coffee');
child_process.fork = function(path, args, options) {
if (helpers.isCoffee(path)) {
if (!Array.isArray(args)) {
options = args || {};
args = [];
}
args = [path].concat(args);
path = binary;
}
return fork(path, args, options);
};
}
}).call(this);

View File

@@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var CoffeeScript, addHistory, addMultilineHandler, fs, merge, nodeREPL, path, prettyErrorMessage, replDefaults, vm, _ref;
var CoffeeScript, addHistory, addMultilineHandler, fs, merge, nodeREPL, path, replDefaults, updateSyntaxError, vm, _ref;
fs = require('fs');
@@ -12,14 +12,14 @@
CoffeeScript = require('./coffee-script');
_ref = require('./helpers'), merge = _ref.merge, prettyErrorMessage = _ref.prettyErrorMessage;
_ref = require('./helpers'), merge = _ref.merge, updateSyntaxError = _ref.updateSyntaxError;
replDefaults = {
prompt: 'coffee> ',
historyFile: process.env.HOME ? path.join(process.env.HOME, '.coffee_history') : void 0,
historyMaxInputSize: 10240,
"eval": function(input, context, filename, cb) {
var Assign, Block, Literal, Value, ast, err, js, _ref1;
var Assign, Block, Literal, Value, ast, err, js, result, _ref1;
input = input.replace(/\uFF00/g, '\n');
input = input.replace(/^\(([\s\S]*)\n\)$/m, '$1');
_ref1 = require('./nodes'), Block = _ref1.Block, Assign = _ref1.Assign, Value = _ref1.Value, Literal = _ref1.Literal;
@@ -30,10 +30,12 @@
bare: true,
locals: Object.keys(context)
});
return cb(null, vm.runInContext(js, context, filename));
result = context === global ? vm.runInThisContext(js, filename) : vm.runInContext(js, context, filename);
return cb(null, result);
} catch (_error) {
err = _error;
return cb(prettyErrorMessage(err, filename, input, true));
updateSyntaxError(err, input);
return cb(err);
}
}
};
@@ -143,6 +145,8 @@
console.warn("Node 0.8.0+ required for CoffeeScript REPL");
process.exit(1);
}
CoffeeScript.register();
process.argv = ['coffee'].concat(process.argv.slice(2));
opts = merge(replDefaults, opts);
repl = nodeREPL.start(opts);
repl.on('exit', function() {

View File

@@ -1,13 +1,16 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__slice = [].slice;
generate = function(tag, value) {
generate = function(tag, value, origin) {
var tok;
tok = [tag, value];
tok.generated = true;
if (origin) {
tok.origin = origin;
}
return tok;
};
@@ -17,10 +20,9 @@
Rewriter.prototype.rewrite = function(tokens) {
this.tokens = tokens;
this.removeLeadingNewlines();
this.removeMidExpressionNewlines();
this.closeOpenCalls();
this.closeOpenIndexes();
this.addImplicitIndentation();
this.normalizeLines();
this.tagPostfixConditionals();
this.addImplicitBracesAndParens();
this.addLocationDataToGeneratedTokens();
@@ -72,17 +74,6 @@
}
};
Rewriter.prototype.removeMidExpressionNewlines = function() {
return this.scanTokens(function(token, i, tokens) {
var _ref;
if (!(token[0] === 'TERMINATOR' && (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0))) {
return 1;
}
tokens.splice(i, 1);
return 0;
});
};
Rewriter.prototype.closeOpenCalls = function() {
var action, condition;
condition = function(token, i) {
@@ -161,9 +152,9 @@
var stack;
stack = [];
return this.scanTokens(function(token, i, tokens) {
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, nextTag, offset, prevTag, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
tag = token[0];
prevTag = (i > 0 ? tokens[i - 1] : [])[0];
prevTag = (prevToken = i > 0 ? tokens[i - 1] : [])[0];
nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0];
stackTop = function() {
return stack[stack.length - 1];
@@ -219,7 +210,7 @@
ours: true
}
]);
tokens.splice(idx, 0, generate('{', generate(new String('{'))));
tokens.splice(idx, 0, generate('{', generate(new String('{')), token));
if (j == null) {
return i += 1;
}
@@ -227,7 +218,7 @@
endImplicitObject = function(j) {
j = j != null ? j : i;
stack.pop();
tokens.splice(j, 0, generate('}', '}'));
tokens.splice(j, 0, generate('}', '}', token));
return i += 1;
};
if (inImplicitCall() && (tag === 'IF' || tag === 'TRY' || tag === 'FINALLY' || tag === 'CATCH' || tag === 'CLASS' || tag === 'SWITCH')) {
@@ -287,6 +278,7 @@
while (this.tag(s - 2) === 'HERECOMMENT') {
s -= 2;
}
this.insideForDeclaration = nextTag === 'FOR';
startsLine = s === 0 || (_ref2 = this.tag(s - 1), __indexOf.call(LINEBREAKS, _ref2) >= 0) || tokens[s - 1].newLine;
if (stackTop()) {
_ref3 = stackTop(), stackTag = _ref3[0], stackIdx = _ref3[1];
@@ -297,20 +289,17 @@
startImplicitObject(s, !!startsLine);
return forward(2);
}
if (prevTag === 'OUTDENT' && inImplicitCall() && (tag === '.' || tag === '?.' || tag === '::' || tag === '?::')) {
endImplicitCall();
return forward(1);
}
if (inImplicitObject() && __indexOf.call(LINEBREAKS, tag) >= 0) {
stackTop()[2].sameLine = false;
}
if (__indexOf.call(IMPLICIT_END, tag) >= 0) {
newLine = prevTag === 'OUTDENT' || prevToken.newLine;
if (__indexOf.call(IMPLICIT_END, tag) >= 0 || __indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) {
while (inImplicit()) {
_ref4 = stackTop(), stackTag = _ref4[0], stackIdx = _ref4[1], (_ref5 = _ref4[2], sameLine = _ref5.sameLine, startsLine = _ref5.startsLine);
if (inImplicitCall() && prevTag !== ',') {
endImplicitCall();
} else if (inImplicitObject() && sameLine && !startsLine) {
endImplicitObject();
} else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':' && endImplicitObject()) {
} else if (inImplicitObject() && tag === 'TERMINATOR' && prevTag !== ',' && !(startsLine && this.looksObjectish(i + 1))) {
endImplicitObject();
} else {
@@ -318,7 +307,7 @@
}
}
}
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && !this.insideForDeclaration && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
offset = nextTag === 'OUTDENT' ? 1 : 0;
while (inImplicitObject()) {
endImplicitObject(i + offset);
@@ -354,30 +343,32 @@
});
};
Rewriter.prototype.addImplicitIndentation = function() {
Rewriter.prototype.normalizeLines = function() {
var action, condition, indent, outdent, starter;
starter = indent = outdent = null;
condition = function(token, i) {
var _ref, _ref1;
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((_ref1 = token[0]) === 'CATCH' || _ref1 === 'FINALLY') && (starter === '->' || starter === '=>'));
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;
};
action = function(token, i) {
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
};
return this.scanTokens(function(token, i, tokens) {
var j, tag, _i, _ref, _ref1;
var j, tag, _i, _ref, _ref1, _ref2;
tag = token[0];
if (tag === 'TERMINATOR' && this.tag(i + 1) === 'THEN') {
tokens.splice(i, 1);
return 0;
}
if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
tokens.splice.apply(tokens, [i, 0].concat(__slice.call(this.indentation())));
return 2;
if (tag === 'TERMINATOR') {
if (this.tag(i + 1) === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
tokens.splice.apply(tokens, [i, 1].concat(__slice.call(this.indentation())));
return 1;
}
if (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0) {
tokens.splice(i, 1);
return 0;
}
}
if (tag === 'CATCH') {
for (j = _i = 1; _i <= 2; j = ++_i) {
if (!((_ref = this.tag(i + j)) === 'OUTDENT' || _ref === 'TERMINATOR' || _ref === 'FINALLY')) {
if (!((_ref1 = this.tag(i + j)) === 'OUTDENT' || _ref1 === 'TERMINATOR' || _ref1 === 'FINALLY')) {
continue;
}
tokens.splice.apply(tokens, [i + j, 0].concat(__slice.call(this.indentation())));
@@ -386,7 +377,7 @@
}
if (__indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) {
starter = tag;
_ref1 = this.indentation(true), indent = _ref1[0], outdent = _ref1[1];
_ref2 = this.indentation(tokens[i]), indent = _ref2[0], outdent = _ref2[1];
if (starter === 'THEN') {
indent.fromThen = true;
}
@@ -425,17 +416,14 @@
});
};
Rewriter.prototype.indentation = function(implicit) {
Rewriter.prototype.indentation = function(origin) {
var indent, outdent;
if (implicit == null) {
implicit = false;
}
indent = ['INDENT', 2];
outdent = ['OUTDENT', 2];
if (implicit) {
if (origin) {
indent.generated = outdent.generated = true;
}
if (!implicit) {
indent.origin = outdent.origin = origin;
} else {
indent.explicit = outdent.explicit = true;
}
return [indent, outdent];
@@ -466,11 +454,11 @@
EXPRESSION_END.push(INVERSES[left] = rite);
}
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
EXPRESSION_CLOSE = ['CATCH', 'THEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
IMPLICIT_UNSPACED_CALL = ['+', '-'];
@@ -482,4 +470,6 @@
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT'];
CALL_CLOSERS = ['.', '?.', '::', '?::'];
}).call(this);

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var Scope, extend, last, _ref;

View File

@@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.0
(function() {
var LineMap, SourceMap;

View File

@@ -1,35 +1,42 @@
{
"name": "coffee-script",
"description": "Unfancy JavaScript",
"keywords": ["javascript", "language", "coffeescript", "compiler"],
"author": "Jeremy Ashkenas",
"version": "1.6.3",
"licenses": [{
"type": "MIT",
"url": "https://raw.github.com/jashkenas/coffee-script/master/LICENSE"
}],
"engines": {
"node": ">=0.8.0"
"name": "coffee-script",
"description": "Unfancy JavaScript",
"keywords": [
"javascript",
"language",
"coffeescript",
"compiler"
],
"author": "Jeremy Ashkenas",
"version": "1.7.0",
"license": "MIT",
"engines": {
"node": ">=0.8.0"
},
"directories" : {
"lib" : "./lib/coffee-script"
"directories": {
"lib": "./lib/coffee-script"
},
"main" : "./lib/coffee-script/coffee-script",
"bin": {
"coffee": "./bin/coffee",
"cake": "./bin/cake"
"main": "./lib/coffee-script/coffee-script",
"bin": {
"coffee": "./bin/coffee",
"cake": "./bin/cake"
},
"scripts": {
"test": "node ./bin/cake test"
},
"homepage": "http://coffeescript.org",
"bugs": "https://github.com/jashkenas/coffee-script/issues",
"repository": {
"homepage": "http://coffeescript.org",
"bugs": "https://github.com/jashkenas/coffee-script/issues",
"repository": {
"type": "git",
"url": "git://github.com/jashkenas/coffee-script.git"
},
"devDependencies": {
"uglify-js": "~2.2",
"jison": ">=0.2.0"
"uglify-js": "~2.2",
"jison": ">=0.2.0",
"highlight.js": "~8.0.0",
"underscore": "~1.5.2"
},
"dependencies": {
"mkdirp": "~0.3.5"
}
}

1
register.js Normal file
View File

@@ -0,0 +1 @@
require('./lib/coffee-script/register');

View File

@@ -29,10 +29,10 @@ if btoa? and JSON? and unescape? and encodeURIComponent?
options.sourceMap = true
options.inline = true
{js, v3SourceMap} = CoffeeScript.compile code, options
"#{js}\n//@ sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//@ sourceURL=coffeescript"
"#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=coffeescript"
# Load a remote script from the current domain via XHR.
CoffeeScript.load = (url, callback, options = {}) ->
CoffeeScript.load = (url, callback, options = {}, hold = false) ->
options.sourceFiles = [url]
xhr = if window.ActiveXObject
new window.ActiveXObject('Microsoft.XMLHTTP')
@@ -43,10 +43,11 @@ CoffeeScript.load = (url, callback, options = {}) ->
xhr.onreadystatechange = ->
if xhr.readyState is 4
if xhr.status in [0, 200]
CoffeeScript.run xhr.responseText, options
param = [xhr.responseText, options]
CoffeeScript.run param... unless hold
else
throw new Error "Could not load #{url}"
callback() if callback
callback param if callback
xhr.send null
# Activate CoffeeScript in the browser by having it compile and evaluate
@@ -57,19 +58,29 @@ runScripts = ->
coffeetypes = ['text/coffeescript', 'text/literate-coffeescript']
coffees = (s for s in scripts when s.type in coffeetypes)
index = 0
length = coffees.length
do execute = ->
script = coffees[index++]
mediatype = script?.type
if mediatype in coffeetypes
options = {literate: mediatype is 'text/literate-coffeescript'}
execute = ->
param = coffees[index]
if param instanceof Array
CoffeeScript.run param...
index++
execute()
for script, i in coffees
do (script, i) ->
options = literate: script.type is coffeetypes[1]
if script.src
CoffeeScript.load script.src, execute, options
CoffeeScript.load script.src,
(param) ->
coffees[i] = param
execute()
options
true
else
options.sourceFiles = ['embedded']
CoffeeScript.run script.innerHTML, options
execute()
null
coffees[i] = [script.innerHTML, options]
execute()
# Listen for window load, both in decent browsers and in IE.
if window.addEventListener

View File

@@ -13,8 +13,6 @@ helpers = require './helpers'
optparse = require './optparse'
CoffeeScript = require './coffee-script'
existsSync = fs.existsSync or path.existsSync
# Keep track of the list of defined tasks, the accepted options, and so on.
tasks = {}
options = {}
@@ -81,7 +79,7 @@ missingTask = (task) -> fatalError "No such task: #{task}"
# When `cake` is invoked, search in the current and all parent directories
# to find the relevant Cakefile.
cakefileDirectory = (dir) ->
return dir if existsSync path.join dir, 'Cakefile'
return dir if fs.existsSync path.join dir, 'Cakefile'
parent = path.normalize path.join dir, '..'
return cakefileDirectory parent unless parent is dir
throw new Error "Cakefile not found in #{process.cwd()}"

View File

@@ -6,18 +6,28 @@
fs = require 'fs'
vm = require 'vm'
path = require 'path'
child_process = require 'child_process'
{Lexer} = require './lexer'
{parser} = require './parser'
helpers = require './helpers'
SourceMap = require './sourcemap'
# The current CoffeeScript version number.
exports.VERSION = '1.6.3'
exports.VERSION = '1.7.0'
exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md']
# Expose helpers for testing.
exports.helpers = helpers
# Function wrapper to add source file information to SyntaxErrors thrown by the
# lexer/parser/compiler.
withPrettyErrors = (fn) ->
(code, options = {}) ->
try
fn.call @, code, options
catch err
throw helpers.updateSyntaxError err, code, options.filename
# Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
#
# If `options.sourceMap` is specified, then `options.filename` must also be specified. All
@@ -27,8 +37,9 @@ exports.helpers = helpers
# in which case this returns a `{js, v3SourceMap, sourceMap}`
# object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for doing programatic
# lookups.
exports.compile = compile = (code, options = {}) ->
{merge} = helpers
exports.compile = compile = withPrettyErrors (code, options) ->
{merge, extend} = helpers
options = extend {}, options
if options.sourceMap
map = new SourceMap
@@ -50,7 +61,10 @@ exports.compile = compile = (code, options = {}) ->
{noReplace: true})
newLines = helpers.count fragment.code, "\n"
currentLine += newLines
currentColumn = fragment.code.length - (if newLines then fragment.code.lastIndexOf "\n" else 0)
if newLines
currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1)
else
currentColumn += fragment.code.length
# Copy the code from each fragment into the final JavaScript.
js += fragment.code
@@ -68,13 +82,13 @@ exports.compile = compile = (code, options = {}) ->
js
# Tokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens = (code, options) ->
exports.tokens = withPrettyErrors (code, options) ->
lexer.tokenize code, options
# Parse a string of CoffeeScript code or an array of lexed tokens, and
# return the AST. You can then compile it by calling `.compile()` on the root,
# or traverse it by using `.traverseChildren()` with a callback.
exports.nodes = (source, options) ->
exports.nodes = withPrettyErrors (source, options) ->
if typeof source is 'string'
parser.parse lexer.tokenize source, options
else
@@ -84,7 +98,7 @@ exports.nodes = (source, options) ->
# setting `__filename`, `__dirname`, and relative `require()`.
exports.run = (code, options = {}) ->
mainModule = require.main
options.sourceMap ?= true
# Set the filename.
mainModule.filename = process.argv[1] =
if options.filename then fs.realpathSync(options.filename) else '.'
@@ -93,18 +107,18 @@ exports.run = (code, options = {}) ->
mainModule.moduleCache and= {}
# Assign paths for node_modules loading
mainModule.paths = require('module')._nodeModulePaths path.dirname fs.realpathSync options.filename or '.'
dir = if options.fileName
path.dirname fs.realpathSync options.filename
else
fs.realpathSync '.'
mainModule.paths = require('module')._nodeModulePaths dir
# Compile.
if not helpers.isCoffee(mainModule.filename) or require.extensions
answer = compile(code, options)
# Attach sourceMap object to sourceMaps[options.filename] so that
# it is accessible by Error.prepareStackTrace.
do patchStackTrace
sourceMaps[mainModule.filename] = answer.sourceMap
mainModule._compile answer.js, mainModule.filename
else
mainModule._compile code, mainModule.filename
answer = compile code, options
code = answer.js ? answer
mainModule._compile code, mainModule.filename
# Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
# The CoffeeScript REPL uses this to run the input.
@@ -142,54 +156,21 @@ exports.eval = (code, options = {}) ->
else
vm.runInContext js, sandbox
# Load and run a CoffeeScript file for Node, stripping any `BOM`s.
loadFile = (module, filename) ->
exports.register = -> require './register'
exports._compileFile = (filename, sourceMap = no) ->
raw = fs.readFileSync filename, 'utf8'
stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
answer = compile(stripped, {filename, sourceMap: true, literate: helpers.isLiterate filename})
sourceMaps[filename] = answer.sourceMap
module._compile answer.js, filename
# If the installed version of Node supports `require.extensions`, register
# CoffeeScript as an extension.
if require.extensions
for ext in ['.coffee', '.litcoffee', '.coffee.md']
require.extensions[ext] = loadFile
try
answer = compile(stripped, {filename, sourceMap, literate: helpers.isLiterate filename})
catch err
# As the filename and code of a dynamically loaded file will be different
# from the original file compiled with CoffeeScript.run, add that
# information to error so it can be pretty-printed later.
throw helpers.updateSyntaxError err, stripped, filename
# Patch Node's module loader to be able to handle mult-dot extensions.
# This is a horrible thing that should not be required. Perhaps, one day,
# when a truly benevolent dictator comes to rule over the Republik of Node,
# it won't be.
Module = require 'module'
findExtension = (filename) ->
extensions = path.basename(filename).split '.'
# Remove the initial dot from dotfiles.
extensions.shift() if extensions[0] is ''
# Start with the longest possible extension and work our way shortwards.
while extensions.shift()
curExtension = '.' + extensions.join '.'
return curExtension if Module._extensions[curExtension]
'.js'
Module::load = (filename) ->
@filename = filename
@paths = Module._nodeModulePaths path.dirname filename
extension = findExtension filename
Module._extensions[extension](this, filename)
@loaded = true
# If we're on Node, patch `child_process.fork` so that Coffee scripts are able
# to fork both CoffeeScript files, and JavaScript files, directly.
if child_process
{fork} = child_process
child_process.fork = (path, args = [], options = {}) ->
execPath = if helpers.isCoffee(path) then 'coffee' else null
if not Array.isArray args
args = []
options = args or {}
options.execPath or= execPath
fork path, args, options
answer
# Instantiate a Lexer for our use here.
lexer = new Lexer
@@ -202,6 +183,7 @@ parser.lexer =
token = @tokens[@pos++]
if token
[tag, @yytext, @yylloc] = token
@errorToken = token.origin or token
@yylineno = @yylloc.first_line
else
tag = ''
@@ -211,52 +193,29 @@ parser.lexer =
@pos = 0
upcomingInput: ->
""
# Make all the AST nodes visible to the parser.
parser.yy = require './nodes'
# Override Jison's default error handling function.
parser.yy.parseError = (message, {token}) ->
# Disregard Jison's message, it contains redundant line numer information.
message = "unexpected #{if token is 1 then 'end of input' else token}"
# Disregard the token, we take its value directly from the lexer in case
# the error is caused by a generated token which might refer to its origin.
{errorToken, tokens} = parser.lexer
[errorTag, errorText, errorLoc] = errorToken
errorText = if errorToken is tokens[tokens.length - 1]
'end of input'
else if errorTag in ['INDENT', 'OUTDENT']
'indentation'
else
helpers.nameWhitespaceCharacter errorText
# The second argument has a `loc` property, which should have the location
# data for this token. Unfortunately, Jison seems to send an outdated `loc`
# (from the previous token), so we take the location information directly
# from the lexer.
helpers.throwSyntaxError message, parser.lexer.yylloc
# Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p)
# NodeJS / V8 have no support for transforming positions in stack traces using
# sourceMap, so we must monkey-patch Error to display CoffeeScript source
# positions.
patched = false
# Map of filenames -> sourceMap object.
sourceMaps = {}
patchStackTrace = ->
return if patched
patched = true
mainModule = require.main
# (Assigning to a property of the Module object in the normal module cache is
# unsuitable, because node deletes those objects from the cache if an
# exception is thrown in the module body.)
Error.prepareStackTrace = (err, stack) ->
sourceFiles = {}
getSourceMapping = (filename, line, column) ->
sourceMap = sourceMaps[filename]
answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap
if answer then [answer[0] + 1, answer[1] + 1] else null
frames = for frame in stack
break if frame.getFunction() is exports.run
" at #{formatSourcePosition frame, getSourceMapping}"
"#{err.name}: #{err.message ? ''}\n#{frames.join '\n'}\n"
helpers.throwSyntaxError "unexpected #{errorText}", errorLoc
# Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js
# Modified to handle sourceMap
@@ -282,11 +241,10 @@ formatSourcePosition = (frame, getSourceMapping) ->
source = getSourceMapping fileName, line, column
fileLocation =
if source
"#{fileName}:#{source[0]}:#{source[1]}, <js>:#{line}:#{column}"
"#{fileName}:#{source[0]}:#{source[1]}"
else
"#{fileName}:#{line}:#{column}"
functionName = frame.getFunctionName()
isConstructor = frame.isConstructor()
isMethodCall = not (frame.isToplevel() or isConstructor)
@@ -312,3 +270,29 @@ formatSourcePosition = (frame, getSourceMapping) ->
else
fileLocation
# Map of filenames -> sourceMap object.
sourceMaps = {}
# Generates the source map for a coffee file and stores it in the local cache variable.
getSourceMap = (filename) ->
return sourceMaps[filename] if sourceMaps[filename]
return unless path?.extname(filename) in exports.FILE_EXTENSIONS
answer = exports._compileFile filename, true
sourceMaps[filename] = answer.sourceMap
# Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p)
# NodeJS / V8 have no support for transforming positions in stack traces using
# sourceMap, so we must monkey-patch Error to display CoffeeScript source
# positions.
Error.prepareStackTrace = (err, stack) ->
getSourceMapping = (filename, line, column) ->
sourceMap = getSourceMap filename
answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap
if answer then [answer[0] + 1, answer[1] + 1] else null
frames = for frame in stack
break if frame.getFunction() is exports.run
" at #{formatSourcePosition frame, getSourceMapping}"
"#{err.name}: #{err.message ? ''}\n#{frames.join '\n'}\n"

View File

@@ -10,10 +10,10 @@ path = require 'path'
helpers = require './helpers'
optparse = require './optparse'
CoffeeScript = require './coffee-script'
mkdirp = require 'mkdirp'
{spawn, exec} = require 'child_process'
{EventEmitter} = require 'events'
exists = fs.exists or path.exists
useWinPathSep = path.sep is '\\'
# Allow CoffeeScript to emit Node.js events.
@@ -24,7 +24,7 @@ printWarn = (line) -> process.stderr.write line + '\n'
hidden = (file) -> /^\.|~$/.test file
# The help banner that is printed when `coffee` is called without arguments.
# The help banner that is printed in conjunction with `-h`/`--help`.
BANNER = '''
Usage: coffee [options] path/to/script.coffee -- [args]
@@ -42,6 +42,7 @@ SWITCHES = [
['-m', '--map', 'generate source map and save as .map files']
['-n', '--nodes', 'print out the parse tree that the parser produces']
[ '--nodejs [ARGS]', 'pass options directly to the "node" binary']
[ '--no-header', 'suppress the "Generated by" header']
['-o', '--output [DIR]', 'set the output directory for compiled JavaScript']
['-p', '--print', 'print out the compiled JavaScript']
['-s', '--stdio', 'listen for and compile scripts over stdio']
@@ -56,7 +57,7 @@ opts = {}
sources = []
sourceCode = []
notSources = {}
watchers = {}
watchedDirs = {}
optionParser = null
# Run `coffee` by parsing passed options and determining what action to take.
@@ -64,64 +65,94 @@ optionParser = null
# `--` will be passed verbatim to your script as arguments in `process.argv`
exports.run = ->
parseOptions()
return forkNode() if opts.nodejs
return usage() if opts.help
return version() if opts.version
return require('./repl').start() if opts.interactive
if opts.watch and not fs.watch
return printWarn "The --watch feature depends on Node v0.6.0+. You are running #{process.version}."
return compileStdio() if opts.stdio
return compileScript null, sources[0] if opts.eval
return require('./repl').start() unless sources.length
literals = if opts.run then sources.splice 1 else []
# Make the REPL *CLI* use the global context so as to (a) be consistent with the
# `node` REPL CLI and, therefore, (b) make packages that modify native prototypes
# (such as 'colors' and 'sugar') work as expected.
replCliOpts = useGlobal: yes
return forkNode() if opts.nodejs
return usage() if opts.help
return version() if opts.version
return require('./repl').start(replCliOpts) if opts.interactive
return compileStdio() if opts.stdio
return compileScript null, opts.arguments[0] if opts.eval
return require('./repl').start(replCliOpts) unless opts.arguments.length
literals = if opts.run then opts.arguments.splice 1 else []
process.argv = process.argv[0..1].concat literals
process.argv[0] = 'coffee'
for source in sources
compilePath source, yes, path.normalize source
opts.output = path.resolve opts.output if opts.output
opts.join = path.resolve opts.join if opts.join
for source in opts.arguments
source = path.resolve source
compilePath source, yes, source
# Compile a path, which could be a script or a directory. If a directory
# is passed, recursively compile all '.coffee', '.litcoffee', and '.coffee.md'
# extension source files in it and all subdirectories.
compilePath = (source, topLevel, base) ->
fs.stat source, (err, stats) ->
throw err if err and err.code isnt 'ENOENT'
if err?.code is 'ENOENT'
return if source in sources or
watchedDirs[source] or
not topLevel and (notSources[source] or hidden source)
try
stats = fs.statSync source
catch err
if err.code is 'ENOENT'
console.error "File not found: #{source}"
process.exit 1
if stats.isDirectory() and path.dirname(source) isnt 'node_modules'
watchDir source, base if opts.watch
fs.readdir source, (err, files) ->
throw err if err and err.code isnt 'ENOENT'
return if err?.code is 'ENOENT'
index = sources.indexOf source
files = files.filter (file) -> not hidden file
sources[index..index] = (path.join source, file for file in files)
sourceCode[index..index] = files.map -> null
files.forEach (file) ->
compilePath (path.join source, file), no, base
else if topLevel or helpers.isCoffee source
watch source, base if opts.watch
fs.readFile source, (err, code) ->
throw err if err and err.code isnt 'ENOENT'
return if err?.code is 'ENOENT'
compileScript(source, code.toString(), base)
else
throw err
if stats.isDirectory()
if path.basename(source) is 'node_modules'
notSources[source] = yes
removeSource source, base
return
if opts.run
compilePath findDirectoryIndex(source), topLevel, base
return
watchDir source, base if opts.watch
try
files = fs.readdirSync source
catch err
if err.code is 'ENOENT' then return else throw err
for file in files
compilePath (path.join source, file), no, base
else if topLevel or helpers.isCoffee source
sources.push source
sourceCode.push null
delete notSources[source]
watch source, base if opts.watch
try
code = fs.readFileSync source
catch err
if err.code is 'ENOENT' then return else throw err
compileScript(source, code.toString(), base)
else
notSources[source] = yes
findDirectoryIndex = (source) ->
for ext in CoffeeScript.FILE_EXTENSIONS
index = path.join source, "index#{ext}"
try
return index if (fs.statSync index).isFile()
catch err
throw err unless err.code is 'ENOENT'
console.error "Missing index.coffee or index.litcoffee in #{source}"
process.exit 1
# Compile a single source script, containing the given code, according to the
# requested options. If evaluating the script directly sets `__filename`,
# `__dirname` and `module.filename` to be correct relative to the script's path.
compileScript = (file, input, base=null) ->
compileScript = (file, input, base = null) ->
o = opts
options = compileOptions file, base
try
t = task = {file, input, options}
CoffeeScript.emit 'compile', task
if o.tokens then printTokens CoffeeScript.tokens t.input, t.options
else if o.nodes then printLine CoffeeScript.nodes(t.input, t.options).toString().trim()
else if o.run then CoffeeScript.run t.input, t.options
if o.tokens
printTokens CoffeeScript.tokens t.input, t.options
else if o.nodes
printLine CoffeeScript.nodes(t.input, t.options).toString().trim()
else if o.run
CoffeeScript.register()
CoffeeScript.run t.input, t.options
else if o.join and t.file isnt o.join
t.input = helpers.invertLiterate t.input if helpers.isLiterate file
sourceCode[sources.indexOf(t.file)] = t.input
@@ -141,8 +172,7 @@ compileScript = (file, input, base=null) ->
catch err
CoffeeScript.emit 'failure', err, task
return if CoffeeScript.listeners('failure').length
useColors = process.stdout.isTTY and not process.env.NODE_DISABLE_COLORS
message = helpers.prettyErrorMessage err, file or '[stdin]', input, useColors
message = err.stack or "#{err}"
if o.watch
printLine message + '\x07'
else
@@ -173,92 +203,116 @@ compileJoin = ->
# time the file is updated. May be used in combination with other options,
# such as `--print`.
watch = (source, base) ->
prevStats = null
watcher = null
prevStats = null
compileTimeout = null
watchErr = (e) ->
if e.code is 'ENOENT'
return if sources.indexOf(source) is -1
try
rewatch()
compile()
catch e
removeSource source, base, yes
compileJoin()
else throw e
watchErr = (err) ->
throw err unless err.code is 'ENOENT'
return unless source in sources
try
rewatch()
compile()
catch
removeSource source, base
compileJoin()
compile = ->
clearTimeout compileTimeout
compileTimeout = wait 25, ->
fs.stat source, (err, stats) ->
return watchErr err if err
return rewatch() if prevStats and stats.size is prevStats.size and
stats.mtime.getTime() is prevStats.mtime.getTime()
return rewatch() if prevStats and
stats.size is prevStats.size and
stats.mtime.getTime() is prevStats.mtime.getTime()
prevStats = stats
fs.readFile source, (err, code) ->
return watchErr err if err
compileScript(source, code.toString(), base)
rewatch()
try
watcher = fs.watch source, compile
catch e
watchErr e
startWatcher = ->
watcher = fs.watch source
.on 'change', compile
.on 'error', (err) ->
throw err unless err.code is 'EPERM'
removeSource source, base
rewatch = ->
watcher?.close()
watcher = fs.watch source, compile
startWatcher()
try
startWatcher()
catch err
watchErr err
# Watch a directory of files for new additions.
watchDir = (source, base) ->
watcher = null
readdirTimeout = null
try
watcher = fs.watch source, ->
startWatcher = ->
watcher = fs.watch source
.on 'error', (err) ->
throw err unless err.code is 'EPERM'
stopWatcher()
.on 'change', ->
clearTimeout readdirTimeout
readdirTimeout = wait 25, ->
fs.readdir source, (err, files) ->
if err
throw err unless err.code is 'ENOENT'
watcher.close()
return unwatchDir source, base
for file in files when not hidden(file) and not notSources[file]
file = path.join source, file
continue if sources.some (s) -> s.indexOf(file) >= 0
sources.push file
sourceCode.push null
compilePath file, no, base
catch e
throw e unless e.code is 'ENOENT'
try
files = fs.readdirSync source
catch err
throw err unless err.code is 'ENOENT'
return stopWatcher()
for file in files
compilePath (path.join source, file), no, base
unwatchDir = (source, base) ->
prevSources = sources[..]
toRemove = (file for file in sources when file.indexOf(source) >= 0)
removeSource file, base, yes for file in toRemove
return unless sources.some (s, i) -> prevSources[i] isnt s
compileJoin()
stopWatcher = ->
watcher.close()
removeSourceDir source, base
watchedDirs[source] = yes
try
startWatcher()
catch err
throw err unless err.code is 'ENOENT'
removeSourceDir = (source, base) ->
delete watchedDirs[source]
sourcesChanged = no
for file in sources when source is path.dirname file
removeSource file, base
sourcesChanged = yes
compileJoin() if sourcesChanged
# Remove a file from our source list, and source code cache. Optionally remove
# the compiled JS version as well.
removeSource = (source, base, removeJs) ->
removeSource = (source, base) ->
index = sources.indexOf source
sources.splice index, 1
sourceCode.splice index, 1
if removeJs and not opts.join
jsPath = outputPath source, base
exists jsPath, (itExists) ->
if itExists
fs.unlink jsPath, (err) ->
throw err if err and err.code isnt 'ENOENT'
timeLog "removed #{source}"
unless opts.join
silentUnlink outputPath source, base
silentUnlink outputPath source, base, '.map'
timeLog "removed #{source}"
silentUnlink = (path) ->
try
fs.unlinkSync path
catch err
throw err unless err.code in ['ENOENT', 'EPERM']
# Get the corresponding output JavaScript path for a source file.
outputPath = (source, base, extension=".js") ->
basename = helpers.baseFileName source, yes, useWinPathSep
srcDir = path.dirname source
baseDir = if base is '.' then srcDir else srcDir.substring base.length
dir = if opts.output then path.join opts.output, baseDir else srcDir
if not opts.output
dir = srcDir
else if source is base
dir = opts.output
else
dir = path.join opts.output, path.relative base, srcDir
path.join dir, basename + extension
# Write out a JavaScript source file with the compiled code. By default, files
@@ -273,7 +327,7 @@ writeJs = (base, sourcePath, js, jsPath, generatedSourceMap = null) ->
compile = ->
if opts.compile
js = ' ' if js.length <= 0
if generatedSourceMap then js = "#{js}\n/*\n//@ sourceMappingURL=#{helpers.baseFileName sourceMapPath, no, useWinPathSep}\n*/\n"
if generatedSourceMap then js = "#{js}\n//# sourceMappingURL=#{helpers.baseFileName sourceMapPath, no, useWinPathSep}\n"
fs.writeFile jsPath, js, (err) ->
if err
printLine err.message
@@ -283,8 +337,8 @@ writeJs = (base, sourcePath, js, jsPath, generatedSourceMap = null) ->
fs.writeFile sourceMapPath, generatedSourceMap, (err) ->
if err
printLine "Could not write source map: #{err.message}"
exists jsDir, (itExists) ->
if itExists then compile() else exec "mkdir -p #{jsDir}", compile
fs.exists jsDir, (itExists) ->
if itExists then compile() else mkdirp jsDir, compile
# Convenience for cleaner setTimeouts.
wait = (milliseconds, func) -> setTimeout func, milliseconds
@@ -309,9 +363,6 @@ parseOptions = ->
o.compile or= !!o.output
o.run = not (o.compile or o.print or o.map)
o.print = !! (o.print or (o.eval or o.stdio and o.compile))
sources = o.arguments
sourceCode[i] = null for source, i in sources
return
# The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (filename, base) ->
@@ -319,7 +370,7 @@ compileOptions = (filename, base) ->
filename
literate: opts.literate or helpers.isLiterate(filename)
bare: opts.bare
header: opts.compile
header: opts.compile and not opts['no-header']
sourceMap: opts.map
}
if filename
@@ -346,10 +397,11 @@ forkNode = ->
nodeArgs = opts.nodejs.split /\s+/
args = process.argv[1..]
args.splice args.indexOf('--nodejs'), 2
spawn process.execPath, nodeArgs.concat(args),
p = spawn process.execPath, nodeArgs.concat(args),
cwd: process.cwd()
env: process.env
customFds: [0, 1, 2]
p.on 'exit', (code) -> process.exit code
# Print the `--help` usage message and exit. Deprecated switches are not
# shown.

View File

@@ -74,7 +74,6 @@ grammar =
Root: [
o '', -> new Block
o 'Body'
o 'Block TERMINATOR'
]
# Any list of statements and expressions, separated by line breaks or semicolons.
@@ -218,9 +217,10 @@ grammar =
o 'ParamVar', -> new Param $1
o 'ParamVar ...', -> new Param $1, null, on
o 'ParamVar = Expression', -> new Param $1, $3
o '...', -> new Expansion
]
# Function Parameters
# Function Parameters
ParamVar: [
o 'Identifier'
o 'ThisProperty'
@@ -379,6 +379,7 @@ grammar =
Arg: [
o 'Expression'
o 'Splat'
o '...', -> new Expansion
]
# Just simple, comma-separated, required arguments (no fancy syntax). We need
@@ -512,7 +513,7 @@ grammar =
# ambiguity.
IfBlock: [
o 'IF Expression Block', -> new If $2, $3, type: $1
o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5, type: $3
o 'IfBlock ELSE IF Expression Block', -> $1.addElse LOC(3,5) new If $4, $5, type: $3
]
# The full complement of *if* expressions, including postfix one-liner
@@ -532,8 +533,9 @@ grammar =
# rules are necessary.
Operation: [
o 'UNARY Expression', -> new Op $1 , $2
o '- Expression', (-> new Op '-', $2), prec: 'UNARY'
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY'
o 'UNARY_MATH Expression', -> new Op $1 , $2
o '- Expression', (-> new Op '-', $2), prec: 'UNARY_MATH'
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY_MATH'
o '-- SimpleAssignable', -> new Op '--', $2
o '++ SimpleAssignable', -> new Op '++', $2
@@ -547,6 +549,7 @@ grammar =
o 'Expression - Expression', -> new Op '-' , $1, $3
o 'Expression MATH Expression', -> new Op $2, $1, $3
o 'Expression ** Expression', -> new Op $2, $1, $3
o 'Expression SHIFT Expression', -> new Op $2, $1, $3
o 'Expression COMPARE Expression', -> new Op $2, $1, $3
o 'Expression LOGIC Expression', -> new Op $2, $1, $3
@@ -583,6 +586,8 @@ operators = [
['nonassoc', '++', '--']
['left', '?']
['right', 'UNARY']
['right', '**']
['right', 'UNARY_MATH']
['left', 'MATH']
['left', '+', '-']
['left', 'SHIFT']
@@ -593,7 +598,7 @@ operators = [
['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
['right', 'FORIN', 'FOROF', 'BY', 'WHEN']
['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
['right', 'POST_IF']
['left', 'POST_IF']
]
# Wrapping Up

View File

@@ -99,30 +99,30 @@ buildLocationData = (first, last) ->
# object is an AST node, updates that object's locationData.
# The object is returned either way.
exports.addLocationDataFn = (first, last) ->
(obj) ->
if ((typeof obj) is 'object') and (!!obj['updateLocationDataIfMissing'])
obj.updateLocationDataIfMissing buildLocationData(first, last)
(obj) ->
if ((typeof obj) is 'object') and (!!obj['updateLocationDataIfMissing'])
obj.updateLocationDataIfMissing buildLocationData(first, last)
return obj
return obj
# Convert jison location data to a string.
# `obj` can be a token, or a locationData.
exports.locationDataToString = (obj) ->
if ("2" of obj) and ("first_line" of obj[2]) then locationData = obj[2]
else if "first_line" of obj then locationData = obj
if ("2" of obj) and ("first_line" of obj[2]) then locationData = obj[2]
else if "first_line" of obj then locationData = obj
if locationData
"#{locationData.first_line + 1}:#{locationData.first_column + 1}-" +
"#{locationData.last_line + 1}:#{locationData.last_column + 1}"
else
"No location data"
if locationData
"#{locationData.first_line + 1}:#{locationData.first_column + 1}-" +
"#{locationData.last_line + 1}:#{locationData.last_column + 1}"
else
"No location data"
# A `.coffee.md` compatible version of `basename`, that returns the file sans-extension.
exports.baseFileName = (file, stripExt = no, useWinPathSep = no) ->
pathSep = if useWinPathSep then /\\|\// else /\//
parts = file.split(pathSep)
file = parts[parts.length - 1]
return file unless stripExt
return file unless stripExt and file.indexOf('.') >= 0
parts = file.split('.')
parts.pop()
parts.pop() if parts[parts.length - 1] is 'coffee' and parts.length > 1
@@ -134,40 +134,65 @@ exports.isCoffee = (file) -> /\.((lit)?coffee|coffee\.md)$/.test file
# Determine if a filename represents a Literate CoffeeScript file.
exports.isLiterate = (file) -> /\.(litcoffee|coffee\.md)$/.test file
# Throws a SyntaxError with a source file location data attached to it in a
# property called `location`.
# Throws a SyntaxError from a given location.
# The error's `toString` will return an error message following the "standard"
# format <filename>:<line>:<col>: <message> plus the line with the error and a
# marker showing where the error is.
exports.throwSyntaxError = (message, location) ->
location.last_line ?= location.first_line
location.last_column ?= location.first_column
error = new SyntaxError message
error.location = location
error.toString = syntaxErrorToString
# Instead of showing the compiler's stacktrace, show our custom error message
# (this is useful when the error bubbles up in Node.js applications that
# compile CoffeeScript for example).
error.stack = error.toString()
throw error
# Creates a nice error message like, following the "standard" format
# <filename>:<line>:<col>: <message> plus the line with the error and a marker
# showing where the error is.
exports.prettyErrorMessage = (error, fileName, code, useColors) ->
return error.stack or "#{error}" unless error.location
# Update a compiler SyntaxError with source code information if it didn't have
# it already.
exports.updateSyntaxError = (error, code, filename) ->
# Avoid screwing up the `stack` property of other errors (i.e. possible bugs).
if error.toString is syntaxErrorToString
error.code or= code
error.filename or= filename
error.stack = error.toString()
error
{first_line, first_column, last_line, last_column} = error.location
codeLine = code.split('\n')[first_line]
syntaxErrorToString = ->
return Error::toString.call @ unless @code and @location
{first_line, first_column, last_line, last_column} = @location
last_line ?= first_line
last_column ?= first_column
filename = @filename or '[stdin]'
codeLine = @code.split('\n')[first_line]
start = first_column
# Show only the first line on multi-line errors.
end = if first_line is last_line then last_column + 1 else codeLine.length
marker = repeat(' ', start) + repeat('^', end - start)
if useColors
colorize = (str) -> "\x1B[1;31m#{str}\x1B[0m"
# Check to see if we're running on a color-enabled TTY.
if process?
colorsEnabled = process.stdout.isTTY and not process.env.NODE_DISABLE_COLORS
if @colorful ? colorsEnabled
colorize = (str) -> "\x1B[1;31m#{str}\x1B[0m"
codeLine = codeLine[...start] + colorize(codeLine[start...end]) + codeLine[end..]
marker = colorize marker
marker = colorize marker
message = """
#{fileName}:#{first_line + 1}:#{first_column + 1}: error: #{error.message}
#{codeLine}
#{marker}
"""
"""
#{filename}:#{first_line + 1}:#{first_column + 1}: error: #{@message}
#{codeLine}
#{marker}
"""
# Uncomment to add stacktrace.
#message += "\n#{error.stack}"
message
exports.nameWhitespaceCharacter = (string) ->
switch string
when ' ' then 'space'
when '\n' then 'newline'
when '\r' then 'carriage return'
when '\t' then 'tab'
else string

View File

@@ -35,18 +35,19 @@ exports.Lexer = class Lexer
# Before returning the token stream, run it through the [Rewriter](rewriter.html)
# unless explicitly asked not to.
tokenize: (code, opts = {}) ->
@literate = opts.literate # Are we lexing literate CoffeeScript?
@indent = 0 # The current indentation level.
@indebt = 0 # The over-indentation at the current level.
@outdebt = 0 # The under-outdentation at the current level.
@indents = [] # The stack of all current indentation levels.
@ends = [] # The stack for pairing up tokens.
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
@literate = opts.literate # Are we lexing literate CoffeeScript?
@indent = 0 # The current indentation level.
@baseIndent = 0 # The overall minimum indentation level
@indebt = 0 # The over-indentation at the current level.
@outdebt = 0 # The under-outdentation at the current level.
@indents = [] # The stack of all current indentation levels.
@ends = [] # The stack for pairing up tokens.
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
@chunkLine =
opts.line or 0 # The start line for the current @chunk.
opts.line or 0 # The start line for the current @chunk.
@chunkColumn =
opts.column or 0 # The start column of the current @chunk.
opts.column or 0 # The start column of the current @chunk.
code = @clean code # The stripped, cleaned original source code.
# At every position, run through this list of attempted matches,
@@ -83,8 +84,8 @@ exports.Lexer = class Lexer
code = code.slice(1) if code.charCodeAt(0) is BOM
code = code.replace(/\r/g, '').replace TRAILING_SPACES, ''
if WHITESPACE.test code
code = "\n#{code}"
@chunkLine--
code = "\n#{code}"
@chunkLine--
code = invertLiterate code if @literate
code
@@ -185,19 +186,15 @@ exports.Lexer = class Lexer
# Matches strings, including multi-line strings. Ensures that quotation marks
# are balanced within the string's contents, and within nested interpolations.
stringToken: ->
switch @chunk.charAt 0
when "'"
return 0 unless match = SIMPLESTR.exec @chunk
string = match[0]
@token 'STRING', string.replace(MULTILINER, '\\\n'), 0, string.length
when '"'
return 0 unless string = @balancedString @chunk, '"'
if 0 < string.indexOf '#{', 1
@interpolateString string[1...-1], strOffset: 1, lexedLength: string.length
else
@token 'STRING', @escapeLines string, 0, string.length
else
return 0
switch quote = @chunk.charAt 0
when "'" then [string] = SIMPLESTR.exec @chunk
when '"' then string = @balancedString @chunk, '"'
return 0 unless string
trimmed = @removeNewlines string[1...-1]
if quote is '"' and 0 < string.indexOf '#{', 1
@interpolateString trimmed, strOffset: 1, lexedLength: string.length
else
@token 'STRING', quote + @escapeLines(trimmed) + quote, 0, string.length
if octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test string
@error "octal escape sequences #{string} are not allowed"
string.length
@@ -237,24 +234,24 @@ exports.Lexer = class Lexer
# JavaScript and Ruby.
regexToken: ->
return 0 if @chunk.charAt(0) isnt '/'
if match = HEREGEX.exec @chunk
length = @heregexToken match
return length
return length if length = @heregexToken()
prev = last @tokens
return 0 if prev and (prev[0] in (if prev.spaced then NOT_REGEX else NOT_SPACED_REGEX))
return 0 unless match = REGEX.exec @chunk
[match, regex, flags] = match
# Avoid conflicts with floor division operator.
return 0 if regex is '//'
if regex[..1] is '/*' then @error 'regular expressions cannot begin with `*`'
if regex is '//' then regex = '/(?:)/'
@token 'REGEX', "#{regex}#{flags}", 0, match.length
match.length
# Matches multiline extended regular expressions.
heregexToken: (match) ->
heregexToken: ->
return 0 unless match = HEREGEX.exec @chunk
[heregex, body, flags] = match
if 0 > body.indexOf '#{'
re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/')
re = @escapeLines body.replace(HEREGEX_OMIT, '$1$2').replace(/\//g, '\\/'), yes
if re.match /^\*/ then @error 'regular expressions cannot begin with `*`'
@token 'REGEX', "/#{ re or '(?:)' }/#{flags}", 0, heregex.length
return heregex.length
@@ -266,7 +263,7 @@ exports.Lexer = class Lexer
if tag is 'TOKENS'
tokens.push value...
else if tag is 'NEOSTRING'
continue unless value = value.replace HEREGEX_OMIT, ''
continue unless value = value.replace HEREGEX_OMIT, '$1$2'
# Convert NEOSTRING into STRING
value = value.replace /\\/g, '\\\\'
token[0] = 'STRING'
@@ -322,40 +319,51 @@ exports.Lexer = class Lexer
@indebt = size - @indent
@suppressNewlines()
return indent.length
unless @tokens.length
@baseIndent = @indent = size
return indent.length
diff = size - @indent + @outdebt
@token 'INDENT', diff, indent.length - size, size
@indents.push diff
@ends.push 'OUTDENT'
@outdebt = @indebt = 0
@indent = size
else if size < @baseIndent
@error 'missing indentation', indent.length
else
@indebt = 0
@outdentToken @indent - size, noNewlines, indent.length
@indent = size
indent.length
# Record an outdent token or multiple tokens, if we happen to be moving back
# inwards past several recorded indents.
# inwards past several recorded indents. Sets new @indent value.
outdentToken: (moveOut, noNewlines, outdentLength) ->
decreasedIndent = @indent - moveOut
while moveOut > 0
len = @indents.length - 1
if @indents[len] is undefined
lastIndent = @indents[@indents.length - 1]
if not lastIndent
moveOut = 0
else if @indents[len] is @outdebt
else if lastIndent is @outdebt
moveOut -= @outdebt
@outdebt = 0
else if @indents[len] < @outdebt
@outdebt -= @indents[len]
moveOut -= @indents[len]
else if lastIndent < @outdebt
@outdebt -= lastIndent
moveOut -= lastIndent
else
dent = @indents.pop() + @outdebt
moveOut -= dent
if outdentLength and @chunk[outdentLength] in INDENTABLE_CLOSERS
decreasedIndent -= dent - moveOut
moveOut = dent
@outdebt = 0
# pair might call outdentToken, so preserve decreasedIndent
@pair 'OUTDENT'
@token 'OUTDENT', dent, 0, outdentLength
@token 'OUTDENT', moveOut, 0, outdentLength
moveOut -= dent
@outdebt -= moveOut if dent
@tokens.pop() while @value() is ';'
@token 'TERMINATOR', '\n', outdentLength, 0 unless @tag() is 'TERMINATOR' or noNewlines
@indent = decreasedIndent
this
# Matches and consumes non-meaningful whitespace. Tag the previous token
@@ -406,6 +414,7 @@ exports.Lexer = class Lexer
else if value in COMPARE then tag = 'COMPARE'
else if value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN'
else if value in UNARY then tag = 'UNARY'
else if value in UNARY_MATH then tag = 'UNARY_MATH'
else if value in SHIFT then tag = 'SHIFT'
else if value in LOGIC or value is '?' and prev?.spaced then tag = 'LOGIC'
else if prev and not prev.spaced
@@ -518,14 +527,9 @@ exports.Lexer = class Lexer
# current chunk.
interpolateString: (str, options = {}) ->
{heredoc, regex, offsetInChunk, strOffset, lexedLength} = options
offsetInChunk = offsetInChunk || 0
strOffset = strOffset || 0
lexedLength = lexedLength || str.length
# Clip leading \n from heredoc
if heredoc and str.length > 0 and str[0] == '\n'
str = str[1...]
strOffset++
offsetInChunk ||= 0
strOffset ||= 0
lexedLength ||= str.length
# Parse the string.
tokens = []
@@ -540,6 +544,8 @@ exports.Lexer = class Lexer
continue
# NEOSTRING is a fake token. This will be converted to a string below.
tokens.push @makeToken('NEOSTRING', str[pi...i], strOffset + pi) if pi < i
unless errorToken
errorToken = @makeToken '', 'string interpolation', offsetInChunk + i + 1, 2
inner = expr[1...-1]
if inner.length
[line, column] = @getLineAndColumnFromChunk(strOffset + i + 1)
@@ -565,7 +571,9 @@ exports.Lexer = class Lexer
# If the first token is not a string, add a fake empty string to the beginning.
tokens.unshift @makeToken('NEOSTRING', '', offsetInChunk) unless tokens[0][0] is 'NEOSTRING'
@token '(', '(', offsetInChunk, 0 if interpolated = tokens.length > 1
if interpolated = tokens.length > 1
@token '(', '(', offsetInChunk, 0, errorToken
# Push all the tokens
for token, i in tokens
[tag, value] = token
@@ -605,8 +613,7 @@ exports.Lexer = class Lexer
# el.click((event) ->
# el.hide())
#
@indent -= size = last @indents
@outdentToken size, true
@outdentToken last(@indents), true
return @pair tag
@ends.pop()
@@ -659,8 +666,9 @@ exports.Lexer = class Lexer
# not specified, the length of `value` will be used.
#
# Returns the new token.
token: (tag, value, offsetInChunk, length) ->
token: (tag, value, offsetInChunk, length, origin) ->
token = @makeToken tag, value, offsetInChunk, length
token.origin = origin if origin
@tokens.push token
token
@@ -675,26 +683,39 @@ exports.Lexer = class Lexer
# Are we in the midst of an unfinished expression?
unfinished: ->
LINE_CONTINUER.test(@chunk) or
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-',
'**', 'SHIFT', 'RELATION', 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
# Remove newlines from beginning and (non escaped) from end of string literals.
removeNewlines: (str) ->
str.replace(/^\s*\n\s*/, '')
.replace(/([^\\]|\\\\)\s*\n\s*$/, '$1')
# Converts newlines for string literals.
escapeLines: (str, heredoc) ->
str.replace MULTILINER, if heredoc then '\\n' else ''
# Ignore escaped backslashes and remove escaped newlines
str = str.replace /\\[^\S\n]*(\n|\\)\s*/g, (escaped, character) ->
if character is '\n' then '' else escaped
if heredoc
str.replace MULTILINER, '\\n'
else
str.replace /\s*\n\s*/g, ' '
# Constructs a string token by escaping quotes and newlines.
makeString: (body, quote, heredoc) ->
return quote + quote unless body
body = body.replace /\\([\s\S])/g, (match, contents) ->
if contents in ['\n', quote] then contents else match
# Ignore escaped backslashes and unescape quotes
body = body.replace /// \\( #{quote} | \\ ) ///g, (match, contents) ->
if contents is quote then contents else match
body = body.replace /// #{quote} ///g, '\\$&'
quote + @escapeLines(body, heredoc) + quote
# Throws a compiler error on the current position.
error: (message) ->
error: (message, offset = 0) ->
# TODO: Are there some cases we could improve the error line number by
# passing the offset in the chunk where the error happened?
throwSyntaxError message, first_line: @chunkLine, first_column: @chunkColumn
[first_line, first_column] = @getLineAndColumnFromChunk offset
throwSyntaxError message, {first_line, first_column}
# Constants
# ---------
@@ -760,27 +781,27 @@ NUMBER = ///
^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal
///i
HEREDOC = /// ^ ("""|''') ([\s\S]*?) (?:\n[^\n\S]*)? \1 ///
HEREDOC = /// ^ ("""|''') ((?: \\[\s\S] | [^\\] )*?) (?:\n[^\n\S]*)? \1 ///
OPERATOR = /// ^ (
?: [-=]> # function
| [-+*/%<>&|^!?=]= # compound assign / compare
| >>>=? # zero-fill right shift
| ([-+:])\1 # doubles
| ([&|<>])\2=? # logic / shift
| ([&|<>*/%])\2=? # logic / shift / power / floor division / modulo
| \?(\.|::) # soak access
| \.{2,3} # range or splat
) ///
WHITESPACE = /^[^\n\S]+/
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/
CODE = /^[-=]>/
MULTI_DENT = /^(?:\n[^\n\S]*)+/
SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/
SIMPLESTR = /^'[^\\']*(?:\\[\s\S][^\\']*)*'/
JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/
@@ -799,9 +820,13 @@ REGEX = /// ^
/) ([imgy]{0,4}) (?!\w)
///
HEREGEX = /// ^ /{3} ([\s\S]+?) /{3} ([imgy]{0,4}) (?!\w) ///
HEREGEX = /// ^ /{3} ((?:\\?[\s\S])+?) /{3} ([imgy]{0,4}) (?!\w) ///
HEREGEX_OMIT = /\s+(?:#.*)?/g
HEREGEX_OMIT = ///
((?:\\\\)+) # consume (and preserve) an even number of backslashes
| \\(\s|/) # preserve escaped whitespace and "de-escape" slashes
| \s+(?:#.*)? # remove whitespace and comments
///g
# Token cleaning regexes.
MULTILINER = /\n/g
@@ -816,23 +841,26 @@ TRAILING_SPACES = /\s+$/
# Compound assignment tokens.
COMPOUND_ASSIGN = [
'-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='
'-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>='
'&=', '^=', '|=', '**=', '//=', '%%='
]
# Unary tokens.
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO']
UNARY = ['NEW', 'TYPEOF', 'DELETE', 'DO']
UNARY_MATH = ['!', '~']
# Logical tokens.
LOGIC = ['&&', '||', '&', '|', '^']
LOGIC = ['&&', '||', '&', '|', '^']
# Bit-shifting tokens.
SHIFT = ['<<', '>>', '>>>']
SHIFT = ['<<', '>>', '>>>']
# Comparison tokens.
COMPARE = ['==', '!=', '<', '>', '<=', '>=']
# Mathematical tokens.
MATH = ['*', '/', '%']
MATH = ['*', '/', '%', '//', '%%']
# Relational tokens that are negatable with `not` prefix.
RELATION = ['IN', 'OF', 'INSTANCEOF']
@@ -862,3 +890,6 @@ INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL', 'NULL', 'UNDEFINED'
# occurs at the start of a line. We disambiguate these from trailing whens to
# avoid an ambiguity in the grammar.
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']
# Additional indent in front of these is ignored.
INDENTABLE_CLOSERS = [')', '}', ']']

View File

@@ -79,7 +79,17 @@ exports.Base = class Base
if jumpNode = @jumps()
jumpNode.error 'cannot use a pure statement in an expression'
o.sharedScope = yes
Closure.wrap(this).compileNode o
func = new Code [], Block.wrap [this]
args = []
if (argumentsNode = @contains isLiteralArguments) or @contains isLiteralThis
args = [new Literal 'this']
if argumentsNode
meth = 'apply'
args.push new Literal 'arguments'
else
meth = 'call'
func = new Value func, [new Access new Literal meth]
(new Call func, args).compileNode o
# If the code generation wishes to use the result of a complex expression
# in multiple places, ensure that the expression is only ever evaluated once,
@@ -176,7 +186,8 @@ exports.Base = class Base
# For this node and all descendents, set the location data to `locationData`
# if the location data is not already set.
updateLocationDataIfMissing: (locationData) ->
@locationData or= locationData
return this if @locationData
@locationData = locationData
@eachChild (child) ->
child.updateLocationDataIfMissing locationData
@@ -242,7 +253,7 @@ exports.Block = class Block extends Base
jumps: (o) ->
for exp in @expressions
return exp if exp.jumps o
return jumpNode if jumpNode = exp.jumps o
# A Block node does not return its entire body, rather it
# ensures that the final expression is returned.
@@ -468,17 +479,25 @@ exports.Value = class Value extends Base
hasProperties: ->
!!@properties.length
bareLiteral: (type) ->
not @properties.length and @base instanceof type
# Some boolean checks for the benefit of other nodes.
isArray : -> not @properties.length and @base instanceof Arr
isArray : -> @bareLiteral(Arr)
isRange : -> @bareLiteral(Range)
isComplex : -> @hasProperties() or @base.isComplex()
isAssignable : -> @hasProperties() or @base.isAssignable()
isSimpleNumber : -> @base instanceof Literal and SIMPLENUM.test @base.value
isString : -> @base instanceof Literal and IS_STRING.test @base.value
isSimpleNumber : -> @bareLiteral(Literal) and SIMPLENUM.test @base.value
isString : -> @bareLiteral(Literal) and IS_STRING.test @base.value
isRegex : -> @bareLiteral(Literal) and IS_REGEX.test @base.value
isAtomic : ->
for node in @properties.concat @base
return no if node.soak or node instanceof Call
yes
isNotCallable : -> @isSimpleNumber() or @isString() or @isRegex() or
@isArray() or @isRange() or @isSplice() or @isObject()
isStatement : (o) -> not @properties.length and @base.isStatement o
assigns : (name) -> not @properties.length and @base.assigns name
jumps : (o) -> not @properties.length and @base.jumps o
@@ -490,6 +509,10 @@ exports.Value = class Value extends Base
isSplice: ->
last(@properties) instanceof Slice
looksStatic: (className) ->
@base.value is className and @properties.length and
@properties[0].name?.value isnt 'prototype'
# The value can be unwrapped as its inner node, if there are no attached
# properties.
unwrap: ->
@@ -555,9 +578,10 @@ exports.Comment = class Comment extends Base
makeReturn: THIS
compileNode: (o, level) ->
code = "/*#{multident @comment, @tab}#{if '\n' in @comment then "\n#{@tab}" else ''}*/\n"
comment = @comment.replace /^(\s*)#/gm, "$1 *"
code = "/*#{multident comment, @tab}#{if '\n' in comment then "\n#{@tab}" else ''} */"
code = o.indent + code if (level or o.level) is LEVEL_TOP
[@makeCode code]
[@makeCode("\n"), @makeCode(code)]
#### Call
@@ -568,6 +592,8 @@ exports.Call = class Call extends Base
@isNew = false
@isSuper = variable is 'super'
@variable = if @isSuper then null else variable
if variable instanceof Value and variable.isNotCallable()
variable.error "literal is not a function"
children: ['variable', 'args']
@@ -768,8 +794,8 @@ exports.Range = class Range extends Base
[@fromC, @fromVar] = @cacheToCodeFragments @from.cache o, LEVEL_LIST
[@toC, @toVar] = @cacheToCodeFragments @to.cache o, LEVEL_LIST
[@step, @stepVar] = @cacheToCodeFragments step.cache o, LEVEL_LIST if step = del o, 'step'
[@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
@stepNum = @stepVar.match(SIMPLENUM) if @stepVar
[@fromNum, @toNum] = [@fromVar.match(NUMBER), @toVar.match(NUMBER)]
@stepNum = @stepVar.match(NUMBER) if @stepVar
# When compiled normally, the range returns the contents of the *for loop*
# needed to iterate over the values in the range. Used by comprehensions.
@@ -789,9 +815,9 @@ exports.Range = class Range extends Base
# Generate the condition.
condPart = if @stepNum
if +@stepNum > 0 then "#{lt} #{@toVar}" else "#{gt} #{@toVar}"
if parseNum(@stepNum[0]) > 0 then "#{lt} #{@toVar}" else "#{gt} #{@toVar}"
else if known
[from, to] = [+@fromNum, +@toNum]
[from, to] = [parseNum(@fromNum[0]), parseNum(@toNum[0])]
if from <= to then "#{lt} #{to}" else "#{gt} #{to}"
else
cond = if @stepVar then "#{@stepVar} > 0" else "#{@fromVar} <= #{@toVar}"
@@ -836,7 +862,7 @@ exports.Range = class Range extends Base
cond = "#{@fromVar} <= #{@toVar}"
body = "var #{vars}; #{cond} ? #{i} <#{@equals} #{@toVar} : #{i} >#{@equals} #{@toVar}; #{cond} ? #{i}++ : #{i}--"
post = "{ #{result}.push(#{i}); }\n#{idt}return #{result};\n#{o.indent}"
hasArgs = (node) -> node?.contains (n) -> n instanceof Literal and n.value is 'arguments' and not n.asKey
hasArgs = (node) -> node?.contains isLiteralArguments
args = ', arguments' if hasArgs(@from) or hasArgs(@to)
[@makeCode "(function() {#{pre}\n#{idt}for (#{body})#{post}}).apply(this#{args ? ''})"]
@@ -1009,13 +1035,11 @@ exports.Class = class Class extends Base
if func instanceof Code
assign = @ctor = func
else
@externalCtor = o.scope.freeVariable 'class'
@externalCtor = o.classScope.freeVariable 'class'
assign = new Assign new Literal(@externalCtor), func
else
if assign.variable.this
func.static = yes
if func.bound
func.context = name
else
assign.variable = new Value(new Literal(name), [(new Access new Literal 'prototype'), new Access base])
if func instanceof Code and func.bound
@@ -1024,14 +1048,17 @@ exports.Class = class Class extends Base
assign
compact exprs
# Walk the body of the class, looking for prototype properties to be converted.
# Walk the body of the class, looking for prototype properties to be converted
# and tagging static assignments.
walkBody: (name, o) ->
@traverseChildren false, (child) =>
cont = true
return false if child instanceof Class
if child instanceof Block
for node, i in exps = child.expressions
if node instanceof Value and node.isObject(true)
if node instanceof Assign and node.variable.looksStatic name
node.value.static = yes
else if node instanceof Value and node.isObject(true)
cont = false
exps[i] = @addProperties node, name, o
child.expressions = exps = flatten exps
@@ -1049,55 +1076,52 @@ exports.Class = class Class extends Base
# Make sure that a constructor is defined for the class, and properly
# configured.
ensureConstructor: (name, o) ->
missing = not @ctor
@ctor or= new Code
ensureConstructor: (name) ->
if not @ctor
@ctor = new Code
if @externalCtor
@ctor.body.push new Literal "#{@externalCtor}.apply(this, arguments)"
else if @parent
@ctor.body.push new Literal "#{name}.__super__.constructor.apply(this, arguments)"
@ctor.body.makeReturn()
@body.expressions.unshift @ctor
@ctor.ctor = @ctor.name = name
@ctor.klass = null
@ctor.noReturn = yes
if missing
superCall = new Literal "#{name}.__super__.constructor.apply(this, arguments)" if @parent
superCall = new Literal "#{@externalCtor}.apply(this, arguments)" if @externalCtor
if superCall
ref = new Literal o.scope.freeVariable 'ref'
@ctor.body.unshift new Assign ref, superCall
@addBoundFunctions o
if superCall
@ctor.body.push ref
@ctor.body.makeReturn()
@body.expressions.unshift @ctor
else
@addBoundFunctions o
# Instead of generating the JavaScript string directly, we build up the
# equivalent syntax tree and compile that, in pieces. You can see the
# constructor, property assignments, and inheritance getting built out below.
compileNode: (o) ->
decl = @determineName()
name = decl or '_Class'
name = "_#{name}" if name.reserved
if jumpNode = @body.jumps()
jumpNode.error 'Class bodies cannot contain pure statements'
if argumentsNode = @body.contains isLiteralArguments
argumentsNode.error "Class bodies shouldn't reference arguments"
name = @determineName() or '_Class'
name = "_#{name}" if name.reserved
lname = new Literal name
func = new Code [], Block.wrap [@body]
args = []
o.classScope = func.makeScope o.scope
@hoistDirectivePrologue()
@setContext name
@walkBody name, o
@ensureConstructor name, o
@ensureConstructor name
@addBoundFunctions o
@body.spaced = yes
@body.expressions.unshift @ctor unless @ctor instanceof Code
@body.expressions.push lname
@body.expressions.unshift @directives...
call = Closure.wrap @body
if @parent
@superClass = new Literal o.scope.freeVariable 'super', no
@body.expressions.unshift new Extends lname, @superClass
call.args.push @parent
params = call.variable.params or call.variable.base.params
params.push new Param @superClass
superClass = new Literal o.classScope.freeVariable 'super', no
@body.expressions.unshift new Extends lname, superClass
func.params.push new Param superClass
args.push @parent
klass = new Parens call, yes
@body.expressions.unshift @directives...
klass = new Parens new Call func, args
klass = new Assign @variable, klass if @variable
klass.compileToFragments o
@@ -1133,6 +1157,7 @@ exports.Assign = class Assign extends Base
return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @context in ['||=', '&&=', '?=']
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
compiledName = @variable.compileToFragments o, LEVEL_LIST
name = fragmentsToText compiledName
unless @context
@@ -1145,8 +1170,8 @@ exports.Assign = class Assign extends Base
else
o.scope.find name
if @value instanceof Code and match = METHOD_DEF.exec name
@value.klass = match[1] if match[1]
@value.name = match[2] ? match[3] ? match[4] ? match[5]
@value.klass = match[1] if match[2]
@value.name = match[3] ? match[4] ? match[5]
val = @value.compileToFragments o, LEVEL_LIST
return (compiledName.concat @makeCode(": "), val) if @context is 'object'
answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
@@ -1179,10 +1204,10 @@ exports.Assign = class Assign extends Base
if obj.unwrap().value in RESERVED
obj.error "assignment to a reserved word: #{obj.compile o}"
return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
vvar = value.compileToFragments o, LEVEL_LIST
vvar = value.compileToFragments o, LEVEL_LIST
vvarText = fragmentsToText vvar
assigns = []
splat = false
assigns = []
expandedIdx = false
# Make vvar into a simple variable if it isn't already.
if not IDENTIFIER.test(vvarText) or @variable.assigns(vvarText)
assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...]
@@ -1201,7 +1226,7 @@ exports.Assign = class Assign extends Base
[obj, idx] = new Value(obj.unwrapAll()).cacheReference o
else
idx = if obj.this then obj.properties[0].name else obj
if not splat and obj instanceof Splat
if not expandedIdx and obj instanceof Splat
name = obj.name.unwrap().value
obj = obj.unwrap()
val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice' }.call(#{vvarText}, #{i}"
@@ -1211,13 +1236,23 @@ exports.Assign = class Assign extends Base
else
val += ") : []"
val = new Literal val
splat = "#{ivar}++"
expandedIdx = "#{ivar}++"
else if not expandedIdx and obj instanceof Expansion
if rest = olen - i - 1
if rest is 1
expandedIdx = "#{vvarText}.length - 1"
else
ivar = o.scope.freeVariable 'i'
val = new Literal "#{ivar} = #{vvarText}.length - #{rest}"
expandedIdx = "#{ivar}++"
assigns.push val.compileToFragments o, LEVEL_LIST
continue
else
name = obj.unwrap().value
if obj instanceof Splat
obj.error "multiple splats are disallowed in an assignment"
if obj instanceof Splat or obj instanceof Expansion
obj.error "multiple splats/expansions are disallowed in an assignment"
if typeof idx is 'number'
idx = new Literal splat or idx
idx = new Literal expandedIdx or idx
acc = no
else
acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
@@ -1238,8 +1273,18 @@ exports.Assign = class Assign extends Base
if not left.properties.length and left.base instanceof Literal and
left.base.value != "this" and not o.scope.check left.base.value
@variable.error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been declared before"
if "?" in @context then o.isExistentialEquals = true
new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
if "?" in @context
o.isExistentialEquals = true
new If(new Existence(left), right, type: 'if').addElse(new Assign(right, @value, '=')).compileToFragments o
else
fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
if o.level <= LEVEL_LIST then fragments else @wrapInBraces fragments
# 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
new Assign(left, new Op(@context[...-1], right, @value)).compileToFragments o
# Compile the assignment from an array splice literal, using JavaScript's
# `Array#splice` method.
@@ -1251,8 +1296,9 @@ exports.Assign = class Assign extends Base
else
fromDecl = fromRef = '0'
if to
if from?.isSimpleNumber() and to.isSimpleNumber()
to = +to.compile(o) - +fromRef
if from instanceof Value and from.isSimpleNumber() and
to instanceof Value and to.isSimpleNumber()
to = to.compile(o) - fromRef
to += 1 unless exclusive
else
to = to.compile(o, LEVEL_ACCESS) + ' - ' + fromRef
@@ -1273,7 +1319,6 @@ exports.Code = class Code extends Base
@params = params or []
@body = body or new Block
@bound = tag is 'boundfunc'
@context = '_this' if @bound
children: ['params', 'body']
@@ -1281,23 +1326,37 @@ exports.Code = class Code extends Base
jumps: NO
makeScope: (parentScope) -> new Scope parentScope, @body, this
# Compilation creates a new scope unless explicitly asked to share with the
# outer scope. Handles splat parameters in the parameter list by peeking at
# the JavaScript `arguments` object. If the function is bound with the `=>`
# arrow, generates a wrapper that saves the current value of `this` through
# a closure.
compileNode: (o) ->
o.scope = new Scope o.scope, @body, this
if @bound and o.scope.method?.bound
@context = o.scope.method.context
# Handle bound functions early.
if @bound and not @context
@context = '_this'
wrapper = new Code [new Param new Literal @context], new Block [this]
boundfunc = new Call(wrapper, [new Literal 'this'])
boundfunc.updateLocationDataIfMissing @locationData
return boundfunc.compileNode(o)
o.scope = del(o, 'classScope') or @makeScope o.scope
o.scope.shared = del(o, 'sharedScope')
o.indent += TAB
delete o.bare
delete o.isExistentialEquals
params = []
exprs = []
@eachParamName (name) -> # this step must be performed before the others
unless o.scope.check name then o.scope.parameter name
for param in @params when param.splat
for {name: p} in @params
for param in @params when param not instanceof Expansion
o.scope.parameter param.asReference o
for param in @params when param.splat or param instanceof Expansion
for {name: p} in @params when param not instanceof Expansion
if p.this then p = p.properties[0].name
if p.value then o.scope.add p.value, 'var', yes
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
@@ -1326,12 +1385,6 @@ exports.Code = class Code extends Base
node.error "multiple parameters named '#{name}'" if name in uniqs
uniqs.push name
@body.makeReturn() unless wasEmpty or @noReturn
if @bound
if o.scope.parent.method?.bound
@bound = @context = o.scope.parent.method.context
else if not @static
o.scope.parent.assign '_this', 'this'
idt = o.indent
code = 'function'
code += ' ' + @name if @ctor
code += '('
@@ -1380,6 +1433,7 @@ exports.Param = class Param extends Base
node = new Literal o.scope.freeVariable 'arg'
node = new Value node
node = new Splat node if @splat
node.updateLocationDataIfMissing @locationData
@reference = node
isComplex: ->
@@ -1416,7 +1470,7 @@ exports.Param = class Param extends Base
atParam obj
# * simple destructured parameters {foo}
else iterator obj.base.value, obj.base
else
else if obj not instanceof Expansion
obj.error "illegal parameter #{obj.compile()}"
return
@@ -1467,6 +1521,22 @@ exports.Splat = class Splat extends Base
concatPart = list[index].joinFragmentArrays args, ', '
[].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, (last list).makeCode(")")
#### Expansion
# Used to skip values inside an array destructuring (pattern matching) or
# parameter list.
exports.Expansion = class Expansion extends Base
isComplex: NO
compileNode: (o) ->
@error 'Expansion must be used inside a destructuring assignment or parameter list'
asReference: (o) ->
this
eachName: (iterator) ->
#### While
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
@@ -1495,7 +1565,7 @@ exports.While = class While extends Base
{expressions} = @body
return no unless expressions.length
for node in expressions
return node if node.jumps loop: yes
return jumpNode if jumpNode = node.jumps loop: yes
no
# The main difference from a JavaScript *while* is that the CoffeeScript
@@ -1624,10 +1694,16 @@ exports.Op = class Op extends Base
@error "cannot increment/decrement \"#{@first.unwrapAll().value}\""
return @compileUnary o if @isUnary()
return @compileChain o if isChain
return @compileExistence o if @operator is '?'
answer = [].concat @first.compileToFragments(o, LEVEL_OP), @makeCode(' ' + @operator + ' '),
@second.compileToFragments(o, LEVEL_OP)
if o.level <= LEVEL_OP then answer else @wrapInBraces answer
switch @operator
when '?' then @compileExistence o
when '**' then @compilePower o
when '//' then @compileFloorDivision o
when '%%' then @compileModulo o
else
lhs = @first.compileToFragments o, LEVEL_OP
rhs = @second.compileToFragments o, LEVEL_OP
answer = [].concat lhs, @makeCode(" #{@operator} "), rhs
if o.level <= LEVEL_OP then answer else @wrapInBraces answer
# Mimic Python's chained comparisons when multiple comparison operators are
# used sequentially. For example:
@@ -1643,7 +1719,7 @@ exports.Op = class Op extends Base
# Keep reference to the left expression, unless this an existential assignment
compileExistence: (o) ->
if !o.isExistentialEquals and @first.isComplex()
if @first.isComplex()
ref = new Literal o.scope.freeVariable 'ref'
fst = new Parens new Assign ref, @first
else
@@ -1670,6 +1746,20 @@ exports.Op = class Op extends Base
parts.reverse() if @flip
@joinFragmentArrays parts, ''
compilePower: (o) ->
# Make a Math.pow call
pow = new Value new Literal('Math'), [new Access new Literal 'pow']
new Call(pow, [@first, @second]).compileToFragments o
compileFloorDivision: (o) ->
floor = new Value new Literal('Math'), [new Access new Literal 'floor']
div = new Op '/', @first, @second
new Call(floor, [div]).compileToFragments o
compileModulo: (o) ->
mod = new Value new Literal utility 'modulo'
new Call(mod, [@first, @second]).compileToFragments o
toString: (idt) ->
super idt, @constructor.name + ' ' + @operator
@@ -1682,7 +1772,7 @@ exports.In = class In extends Base
invert: NEGATE
compileNode: (o) ->
if @array instanceof Value and @array.isArray()
if @array instanceof Value and @array.isArray() and @array.base.objects.length
for obj in @array.base.objects when obj instanceof Splat
hasSplat = yes
break
@@ -1691,7 +1781,6 @@ exports.In = class In extends Base
@compileLoopTest o
compileOrTest: (o) ->
return [@makeCode("#{!!@negated}")] if @array.base.objects.length is 0
[sub, ref] = @object.cache o, LEVEL_OP
[cmp, cnj] = if @negated then [' !== ', ' && '] else [' === ', ' || ']
tests = []
@@ -1837,7 +1926,7 @@ exports.For = class For extends While
@pattern = @name instanceof Value
@index.error 'indexes do not apply to range loops' if @range and @index
@name.error 'cannot pattern match over range loops' if @range and @pattern
@index.error 'cannot use own with for-in' if @own and not @object
@name.error 'cannot use own with for-in' if @own and not @object
@returns = false
children: ['body', 'source', 'guard', 'step']
@@ -1852,7 +1941,7 @@ exports.For = class For extends While
@returns = no if lastJumps and lastJumps instanceof Return
source = if @range then @source.base else @source
scope = o.scope
name = @name and (@name.compile o, LEVEL_LIST)
name = @name and (@name.compile o, LEVEL_LIST) if not @pattern
index = @index and (@index.compile o, LEVEL_LIST)
scope.find(name) if name and not @pattern
scope.find(index) if index
@@ -1862,7 +1951,7 @@ exports.For = class For extends While
kvarAssign = if kvar isnt ivar then "#{kvar} = " else ""
if @step and not @range
[step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST
stepNum = stepVar.match SIMPLENUM
stepNum = stepVar.match NUMBER
name = ivar if @pattern
varPart = ''
guardPart = ''
@@ -1879,7 +1968,7 @@ exports.For = class For extends While
namePart = "#{name} = #{svar}[#{kvar}]"
if not @object
defPart += "#{@tab}#{step};\n" if step isnt stepVar
lvar = scope.freeVariable 'len' unless @step and stepNum and down = (+stepNum < 0)
lvar = scope.freeVariable 'len' unless @step and stepNum and down = (parseNum(stepNum[0]) < 0)
declare = "#{kvarAssign}#{ivar} = 0, #{lvar} = #{svar}.length"
declareDown = "#{kvarAssign}#{ivar} = #{svar}.length - 1"
compare = "#{ivar} < #{lvar}"
@@ -1924,7 +2013,7 @@ exports.For = class For extends While
for expr, idx in body.expressions
expr = expr.unwrapAll()
continue unless expr instanceof Call
val = expr.variable.unwrapAll()
val = expr.variable?.unwrapAll()
continue unless (val instanceof Code) or
(val instanceof Value and
val.base?.unwrapAll() instanceof Code and
@@ -1951,7 +2040,7 @@ exports.Switch = class Switch extends Base
jumps: (o = {block: yes}) ->
for [conds, block] in @cases
return block if block.jumps o
return jumpNode if jumpNode = block.jumps o
@otherwise?.jumps o
makeReturn: (res) ->
@@ -2006,6 +2095,7 @@ exports.If = class If extends Base
else
@isChain = elseBody instanceof If
@elseBody = @ensureBlock elseBody
@elseBody.updateLocationDataIfMissing elseBody.locationData
this
# The **If** only compiles into a statement if either of its bodies needs
@@ -2062,50 +2152,6 @@ exports.If = class If extends Base
unfoldSoak: ->
@soak and this
# Faux-Nodes
# ----------
# Faux-nodes are never created by the grammar, but are used during code
# generation to generate other combinations of nodes.
#### Closure
# A faux-node used to wrap an expressions body in a closure.
Closure =
# Wrap the expressions body, unless it contains a pure statement,
# in which case, no dice. If the body mentions `this` or `arguments`,
# then make sure that the closure wrapper preserves the original values.
wrap: (expressions, statement, noReturn) ->
return expressions if expressions.jumps()
func = new Code [], Block.wrap [expressions]
args = []
argumentsNode = expressions.contains @isLiteralArguments
if argumentsNode and expressions.classBody
argumentsNode.error "Class bodies shouldn't reference arguments"
if argumentsNode or expressions.contains @isLiteralThis
meth = new Literal if argumentsNode then 'apply' else 'call'
args = [new Literal 'this']
args.push new Literal 'arguments' if argumentsNode
func = new Value func, [new Access meth]
func.noReturn = noReturn
call = new Call func, args
if statement then Block.wrap [call] else call
isLiteralArguments: (node) ->
node instanceof Literal and node.value is 'arguments' and not node.asKey
isLiteralThis: (node) ->
(node instanceof Literal and node.value is 'this' and not node.asKey) or
(node instanceof Code and node.bound) or
(node instanceof Call and node.isSuper)
# Unfold a node's child if soak, then tuck the node under created `If`
unfoldSoak = (o, parent, name) ->
return unless ifn = parent[name].unfoldSoak o
parent[name] = ifn.body
ifn.body = new Value parent
ifn
# Constants
# ---------
@@ -2113,18 +2159,42 @@ UTILITIES =
# Correctly set up a prototype chain for inheritance, including a reference
# to the superclass for `super()` calls, and copies of any static properties.
extends: -> """
function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }
"""
extends: -> "
function(child, parent) {
for (var key in parent) {
if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
}
"
# Create a function bound to the current value of "this".
bind: -> '''
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
'''
bind: -> '
function(fn, me){
return function(){
return fn.apply(me, arguments);
};
}
'
# Discover if an item is in an array.
indexOf: -> """
[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }
indexOf: -> "
[].indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (i in this && this[i] === item) return i;
}
return -1;
}
"
modulo: -> """
function(a, b) { return (a % b + +b) % b; }
"""
# Shortcuts to speed up the lookup time for native functions.
@@ -2146,27 +2216,27 @@ TAB = ' '
IDENTIFIER_STR = "[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*"
IDENTIFIER = /// ^ #{IDENTIFIER_STR} $ ///
SIMPLENUM = /^[+-]?\d+$/
METHOD_DEF = ///
^
(?:
(#{IDENTIFIER_STR})
\.prototype
(?:
\.(#{IDENTIFIER_STR})
| \[("(?:[^\\"\r\n]|\\.)*"|'(?:[^\\'\r\n]|\\.)*')\]
| \[(0x[\da-fA-F]+ | \d*\.?\d+ (?:[eE][+-]?\d+)?)\]
)
)
|
(#{IDENTIFIER_STR})
$
///
HEXNUM = /^[+-]?0x[\da-f]+/i
NUMBER = ///^[+-]?(?:
0x[\da-f]+ | # hex
\d*\.?\d+ (?:e[+-]?\d+)? # decimal
)$///i
# Is a literal value a string?
METHOD_DEF = /// ^
(#{IDENTIFIER_STR})
(\.prototype)?
(?: \.(#{IDENTIFIER_STR})
| \[("(?:[^\\"\r\n]|\\.)*"|'(?:[^\\'\r\n]|\\.)*')\]
| \[(0x[\da-fA-F]+ | \d*\.?\d+ (?:[eE][+-]?\d+)?)\]
)
$ ///
# Is a literal value a string/regex?
IS_STRING = /^['"]/
IS_REGEX = /^\//
# Utility Functions
# -----------------
# Helper Functions
# ----------------
# Helper for ensuring that utility functions are assigned at the top level.
utility = (name) ->
@@ -2177,3 +2247,28 @@ utility = (name) ->
multident = (code, tab) ->
code = code.replace /\n/g, '$&' + tab
code.replace /\s+$/, ''
# Parse a number (+- decimal/hexadecimal)
# Examples: 0, -1, 1, 2e3, 2e-3, -0xfe, 0xfe
parseNum = (x) ->
if not x?
0
else if x.match HEXNUM
parseInt x, 16
else
parseFloat x
isLiteralArguments = (node) ->
node instanceof Literal and node.value is 'arguments' and not node.asKey
isLiteralThis = (node) ->
(node instanceof Literal and node.value is 'this' and not node.asKey) or
(node instanceof Code and node.bound) or
(node instanceof Call and node.isSuper)
# Unfold a node's child if soak, then tuck the node under created `If`
unfoldSoak = (o, parent, name) ->
return unless ifn = parent[name].unfoldSoak o
parent[name] = ifn.body
ifn.body = new Value parent
ifn

52
src/register.coffee Normal file
View File

@@ -0,0 +1,52 @@
CoffeeScript = require './coffee-script'
child_process = require 'child_process'
helpers = require './helpers'
path = require 'path'
# Load and run a CoffeeScript file for Node, stripping any `BOM`s.
loadFile = (module, filename) ->
answer = CoffeeScript._compileFile filename, false
module._compile answer, filename
# If the installed version of Node supports `require.extensions`, register
# CoffeeScript as an extension.
if require.extensions
for ext in CoffeeScript.FILE_EXTENSIONS
require.extensions[ext] = loadFile
# Patch Node's module loader to be able to handle mult-dot extensions.
# This is a horrible thing that should not be required. Perhaps, one day,
# when a truly benevolent dictator comes to rule over the Republik of Node,
# it won't be.
Module = require 'module'
findExtension = (filename) ->
extensions = path.basename(filename).split '.'
# Remove the initial dot from dotfiles.
extensions.shift() if extensions[0] is ''
# Start with the longest possible extension and work our way shortwards.
while extensions.shift()
curExtension = '.' + extensions.join '.'
return curExtension if Module._extensions[curExtension]
'.js'
Module::load = (filename) ->
@filename = filename
@paths = Module._nodeModulePaths path.dirname filename
extension = findExtension filename
Module._extensions[extension](this, filename)
@loaded = true
# If we're on Node, patch `child_process.fork` so that Coffee scripts are able
# to fork both CoffeeScript files, and JavaScript files, directly.
if child_process
{fork} = child_process
binary = require.resolve '../../bin/coffee'
child_process.fork = (path, args, options) ->
if helpers.isCoffee path
unless Array.isArray args
options = args or {}
args = []
args = [path].concat args
path = binary
fork path, args, options

View File

@@ -3,7 +3,7 @@ path = require 'path'
vm = require 'vm'
nodeREPL = require 'repl'
CoffeeScript = require './coffee-script'
{merge, prettyErrorMessage} = require './helpers'
{merge, updateSyntaxError} = require './helpers'
replDefaults =
prompt: 'coffee> ',
@@ -27,9 +27,15 @@ replDefaults =
new Assign (new Value new Literal '_'), ast, '='
]
js = ast.compile bare: yes, locals: Object.keys(context)
cb null, vm.runInContext(js, context, filename)
result = if context is global
vm.runInThisContext js, filename
else
vm.runInContext js, context, filename
cb null, result
catch err
cb prettyErrorMessage(err, filename, input, yes)
# AST's `compile` does not add source code information to syntax errors.
updateSyntaxError err, input
cb err
addMultilineHandler = (repl) ->
{rli, inputStream, outputStream} = repl
@@ -125,6 +131,8 @@ module.exports =
console.warn "Node 0.8.0+ required for CoffeeScript REPL"
process.exit 1
CoffeeScript.register()
process.argv = ['coffee'].concat process.argv[2..]
opts = merge replDefaults, opts
repl = nodeREPL.start opts
repl.on 'exit', -> repl.outputStream.write '\n'

View File

@@ -6,10 +6,11 @@
# parentheses, and generally clean things up.
# Create a generated token: one that exists due to a use of implicit syntax.
generate = (tag, value) ->
tok = [tag, value]
tok.generated = yes
tok
generate = (tag, value, origin) ->
tok = [tag, value]
tok.generated = yes
tok.origin = origin if origin
tok
# The **Rewriter** class is used by the [Lexer](lexer.html), directly against
# its internal array of tokens.
@@ -26,10 +27,9 @@ class exports.Rewriter
# corrected before implicit parentheses can be wrapped around blocks of code.
rewrite: (@tokens) ->
@removeLeadingNewlines()
@removeMidExpressionNewlines()
@closeOpenCalls()
@closeOpenIndexes()
@addImplicitIndentation()
@normalizeLines()
@tagPostfixConditionals()
@addImplicitBracesAndParens()
@addLocationDataToGeneratedTokens()
@@ -65,14 +65,6 @@ class exports.Rewriter
break for [tag], i in @tokens when tag isnt 'TERMINATOR'
@tokens.splice 0, i if i
# Some blocks occur in the middle of expressions -- when we're expecting
# this, remove their trailing newlines.
removeMidExpressionNewlines: ->
@scanTokens (token, i, tokens) ->
return 1 unless token[0] is 'TERMINATOR' and @tag(i + 1) in EXPRESSION_CLOSE
tokens.splice i, 1
0
# The lexer has tagged the opening parenthesis of a method call. Match it with
# its paired close. We have the mis-nested outdent case included here for
# calls that close on the same line, just before their outdent.
@@ -140,7 +132,7 @@ class exports.Rewriter
@scanTokens (token, i, tokens) ->
[tag] = token
[prevTag] = if i > 0 then tokens[i - 1] else []
[prevTag] = prevToken = if i > 0 then tokens[i - 1] else []
[nextTag] = if i < tokens.length - 1 then tokens[i + 1] else []
stackTop = -> stack[stack.length - 1]
startIdx = i
@@ -171,13 +163,13 @@ class exports.Rewriter
startImplicitObject = (j, startsLine = yes) ->
idx = j ? i
stack.push ['{', idx, sameLine: yes, startsLine: startsLine, ours: yes]
tokens.splice idx, 0, generate '{', generate(new String('{'))
tokens.splice idx, 0, generate '{', generate(new String('{')), token
i += 1 if not j?
endImplicitObject = (j) ->
j = j ? i
stack.pop()
tokens.splice j, 0, generate '}', '}'
tokens.splice j, 0, generate '}', '}', token
i += 1
# Don't end an implicit call on next indent if any of these are in an argument
@@ -264,6 +256,9 @@ class exports.Rewriter
if @tag(i - 2) is '@' then s = i - 2 else s = i - 1
s -= 2 while @tag(s - 2) is 'HERECOMMENT'
# Mark if the value is a for loop
@insideForDeclaration = nextTag is 'FOR'
startsLine = s is 0 or @tag(s - 1) in LINEBREAKS or tokens[s - 1].newLine
# Are we just continuing an already declared object?
if stackTop()
@@ -284,13 +279,16 @@ class exports.Rewriter
# c
# .h a
#
if prevTag is 'OUTDENT' and inImplicitCall() and tag in ['.', '?.', '::', '?::']
endImplicitCall()
return forward(1)
# and also
#
# f a
# .g b
# .h a
stackTop()[2].sameLine = no if inImplicitObject() and tag in LINEBREAKS
if tag in IMPLICIT_END
newLine = prevTag is 'OUTDENT' or prevToken.newLine
if tag in IMPLICIT_END or tag in CALL_CLOSERS and newLine
while inImplicit()
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()
# Close implicit calls when reached end of argument list
@@ -298,7 +296,8 @@ class exports.Rewriter
endImplicitCall()
# Close implicit objects such as:
# return a: 1, b: 2 unless true
else if inImplicitObject() and sameLine and not startsLine
else if inImplicitObject() and not @insideForDeclaration and sameLine and
tag isnt 'TERMINATOR' and prevTag isnt ':' and
endImplicitObject()
# Close implicit objects when at end of line, line didn't end with a comma
# and the implicit object didn't start the line or the next line doesn't look like
@@ -323,6 +322,7 @@ class exports.Rewriter
# f a, b: c, d: e, f, g: h: i, j
#
if tag is ',' and not @looksObjectish(i + 1) and inImplicitObject() and
not @insideForDeclaration and
(nextTag isnt 'TERMINATOR' or not @looksObjectish(i + 2))
# When nextTag is OUTDENT the comma is insignificant and
# should just be ignored so embed it in the implicit object.
@@ -341,11 +341,11 @@ class exports.Rewriter
return 1 if token[2]
return 1 unless token.generated or token.explicit
if token[0] is '{' and nextLocation=tokens[i + 1]?[2]
{first_line: line, first_column: column} = nextLocation
{first_line: line, first_column: column} = nextLocation
else if prevLocation = tokens[i - 1]?[2]
{last_line: line, last_column: column} = prevLocation
{last_line: line, last_column: column} = prevLocation
else
line = column = 0
line = column = 0
token[2] =
first_line: line
first_column: column
@@ -355,27 +355,31 @@ class exports.Rewriter
# Because our grammar is LALR(1), it can't handle some single-line
# expressions that lack ending delimiters. The **Rewriter** adds the implicit
# blocks, so it doesn't need to. ')' can close a single-line block,
# but we need to make sure it's balanced.
addImplicitIndentation: ->
# blocks, so it doesn't need to. To keep the grammar clean and tidy, trailing
# newlines within expressions are removed and the indentation tokens of empty
# blocks are added.
normalizeLines: ->
starter = indent = outdent = null
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] in ['CATCH', 'FINALLY'] and starter in ['->', '=>'])
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>']) or
token[0] in CALL_CLOSERS and @tokens[i - 1].newLine
action = (token, i) ->
@tokens.splice (if @tag(i - 1) is ',' then i - 1 else i), 0, outdent
@scanTokens (token, i, tokens) ->
[tag] = token
if tag is 'TERMINATOR' and @tag(i + 1) is 'THEN'
tokens.splice i, 1
return 0
if tag is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
tokens.splice i, 0, @indentation()...
return 2
if tag is 'TERMINATOR'
if @tag(i + 1) is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
tokens.splice i, 1, @indentation()...
return 1
if @tag(i + 1) in EXPRESSION_CLOSE
tokens.splice i, 1
return 0
if tag is 'CATCH'
for j in [1..2] when @tag(i + j) in ['OUTDENT', 'TERMINATOR', 'FINALLY']
tokens.splice i + j, 0, @indentation()...
@@ -383,7 +387,7 @@ class exports.Rewriter
if tag in SINGLE_LINERS and @tag(i + 1) isnt 'INDENT' and
not (tag is 'ELSE' and @tag(i + 1) is 'IF')
starter = tag
[indent, outdent] = @indentation yes
[indent, outdent] = @indentation tokens[i]
indent.fromThen = true if starter is 'THEN'
tokens.splice i + 1, 0, indent
@detectEnd i + 2, condition, action
@@ -413,11 +417,14 @@ class exports.Rewriter
return 1
# Generate the indentation tokens, based on another token on the same line.
indentation: (implicit = no) ->
indentation: (origin) ->
indent = ['INDENT', 2]
outdent = ['OUTDENT', 2]
indent.generated = outdent.generated = yes if implicit
indent.explicit = outdent.explicit = yes if not implicit
if origin
indent.generated = outdent.generated = yes
indent.origin = outdent.origin = origin
else
indent.explicit = outdent.explicit = yes
[indent, outdent]
generate: generate
@@ -452,7 +459,7 @@ for [left, rite] in BALANCED_PAIRS
EXPRESSION_END .push INVERSES[left] = rite
# Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
EXPRESSION_CLOSE = ['CATCH', 'THEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
# Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS']
@@ -460,8 +467,8 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
IMPLICIT_CALL = [
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS'
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER'
'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY',
'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'
]
IMPLICIT_UNSPACED_CALL = ['+', '-']
@@ -477,3 +484,6 @@ SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADIN
# Tokens that end a line.
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT']
# Tokens that close open calls when they follow a newline.
CALL_CLOSERS = ['.', '?.', '::', '?::']

View File

@@ -81,6 +81,9 @@ test "compound assignment should be careful about caching variables", ->
eq 5, base.five
eq 5, count
eq 5, base().five ?= 6
eq 6, count
test "compound assignment with implicit objects", ->
obj = undefined
obj ?=
@@ -265,6 +268,22 @@ test "#2055: destructuring assignment with `new`", ->
{length} = new Array
eq 0, length
test "#156: destructuring with expansion", ->
array = [1..5]
[first, ..., last] = array
eq 1, first
eq 5, last
[..., lastButOne, last] = array
eq 4, lastButOne
eq 5, last
[first, second, ..., last] = array
eq 2, second
[..., last] = 'strings as well -> x'
eq 'x', last
throws (-> CoffeeScript.compile "[1, ..., 3]"), null, "prohibit expansion outside of assignment"
throws (-> CoffeeScript.compile "[..., a, b...] = c"), null, "prohibit expansion and a splat"
throws (-> CoffeeScript.compile "[...] = c"), null, "prohibit lone expansion"
# Existential Assignment
@@ -380,3 +399,9 @@ test "#2613: parens on LHS of destructuring", ->
a = {}
[(a).b] = [1, 2, 3]
eq a.b, 1
test "#2181: conditional assignment as a subexpression", ->
a = false
false && a or= true
eq false, a
eq false, not a or= true

View File

@@ -699,6 +699,15 @@ test "#2052: classes should work in strict mode", ->
catch e
ok no
test "directives in class with extends ", ->
strictTest = """
class extends Object
### comment ###
'use strict'
do -> eq this, undefined
"""
CoffeeScript.run strictTest, bare: yes
test "#2630: class bodies can't reference arguments", ->
throws ->
CoffeeScript.compile('class Test then arguments')
@@ -791,3 +800,31 @@ test "#2796: ditto, ditto, ditto", ->
new Base
eq answer, 'right!'
test "#3063: Class bodies cannot contain pure statements", ->
throws -> CoffeeScript.compile """
class extends S
return if S.f
@f: => this
"""
test "#2949: super in static method with reserved name", ->
class Foo
@static: -> 'baz'
class Bar extends Foo
@static: -> super
eq Bar.static(), 'baz'
test "#3232: super in static methods (not object-assigned)", ->
class Foo
@baz = -> true
@qux = -> true
class Bar extends Foo
@baz = -> super
Bar.qux = -> super
ok Bar.baz()
ok Bar.qux()

View File

@@ -211,3 +211,191 @@ test "#2916: block comment before implicit call with implicit object", ->
### ###
fn
a: yes
test "#3132: Format single-line block comment nicely", ->
input = """
### Single-line block comment without additional space here => ###"""
result = """
/* Single-line block comment without additional space here => */
"""
eq CoffeeScript.compile(input, bare: on), result
test "#3132: Format multi-line block comment nicely", ->
input = """
###
# Multi-line
# block
# comment
###"""
result = """
/*
* Multi-line
* block
* comment
*/
"""
eq CoffeeScript.compile(input, bare: on), result
test "#3132: Format simple block comment nicely", ->
input = """
###
No
Preceding hash
###"""
result = """
/*
No
Preceding hash
*/
"""
eq CoffeeScript.compile(input, bare: on), result
test "#3132: Format indented block-comment nicely", ->
input = """
fn = () ->
###
# Indented
Multiline
###
1"""
result = """
var fn;
fn = function() {
/*
* Indented
Multiline
*/
return 1;
};
"""
eq CoffeeScript.compile(input, bare: on), result
# Although adequately working, block comment-placement is not yet perfect.
# (Considering a case where multiple variables have been declared …)
test "#3132: Format jsdoc-style block-comment nicely", ->
input = """
###*
# Multiline for jsdoc-"@doctags"
#
# @type {Function}
###
fn = () -> 1
"""
result = """
/**
* Multiline for jsdoc-"@doctags"
*
* @type {Function}
*/
var fn;
fn = function() {
return 1;
};
"""
eq CoffeeScript.compile(input, bare: on), result
# Although adequately working, block comment-placement is not yet perfect.
# (Considering a case where multiple variables have been declared …)
test "#3132: Format hand-made (raw) jsdoc-style block-comment nicely", ->
input = """
###*
* Multiline for jsdoc-"@doctags"
*
* @type {Function}
###
fn = () -> 1
"""
result = """
/**
* Multiline for jsdoc-"@doctags"
*
* @type {Function}
*/
var fn;
fn = function() {
return 1;
};
"""
eq CoffeeScript.compile(input, bare: on), result
# Although adequately working, block comment-placement is not yet perfect.
# (Considering a case where multiple variables have been declared …)
test "#3132: Place block-comments nicely", ->
input = """
###*
# A dummy class definition
#
# @class
###
class DummyClass
###*
# @constructor
###
constructor: ->
###*
# Singleton reference
#
# @type {DummyClass}
###
@instance = new DummyClass()
"""
result = """
/**
* A dummy class definition
*
* @class
*/
var DummyClass;
DummyClass = (function() {
/**
* @constructor
*/
function DummyClass() {}
/**
* Singleton reference
*
* @type {DummyClass}
*/
DummyClass.instance = new DummyClass();
return DummyClass;
})();
"""
eq CoffeeScript.compile(input, bare: on), result

Some files were not shown because too many files have changed in this diff Show More