Compare commits

...

381 Commits
0.5.4 ... 0.6.2

Author SHA1 Message Date
Jeremy Ashkenas
485346f0e5 CoffeeScript 0.6.2 2010-05-15 01:18:05 -04:00
Jeremy Ashkenas
a8d4c3a567 sprinkling toString() throughout, for Node.js 0.1.95 compatibility. 2010-05-15 00:34:14 -04:00
Jeremy Ashkenas
dfb3a13246 Fixed lingering CoffeeScript Compiler running live in Internet Explorer bugs. Implemented helpers.index_of and removed named functions. Ticket #366 2010-05-14 23:40:04 -04:00
Jeremy Ashkenas
f84eb9ed47 fixing heredoc indentation from herecomment tweaks 2010-05-14 21:50:17 -04:00
Jeremy Ashkenas
8136c5f3de making regexes stricter about their flags. 2010-05-14 09:14:41 -04:00
Jeremy Ashkenas
45669e08c6 better indentation handling for far-left heredocs and herecomments 2010-05-12 21:49:46 -04:00
Jeremy Ashkenas
a5db69e1af better indentation handling for far-left heredocs and herecomments 2010-05-12 21:47:31 -04:00
Jeremy Ashkenas
8aceef20e1 Adding initial implementation of here-comments (block comments) Issue #368 2010-05-12 20:56:44 -04:00
Jeremy Ashkenas
7e3c71ed19 Allowing more flexible linebreaks/indentation within object literals. 2010-05-12 18:28:40 -04:00
Jeremy Ashkenas
9d09bee6fb better test for sans-condition switch... 2010-05-11 09:32:16 -04:00
Jeremy Ashkenas
cd6ee373ff enabling condition-less switches. 2010-05-10 22:57:51 -04:00
Jeremy Ashkenas
4d0acc9b02 fixing andreyvit's issue with parentheses not being applied to multi-operators as the condition clause of a switch... 2010-05-10 22:50:11 -04:00
Jeremy Ashkenas
393fbf1b66 merging in gfxmonk's cleanup to how children of nodes are determined. Removing some (so far) unused portions. 2010-05-10 22:41:18 -04:00
gfxmonk
3324b03a5d Merge remote branch 'upstream/master'
Conflicts:
	src/nodes.coffee
2010-05-10 21:09:00 +10:00
gfxmonk
eb91f9922d determine @children dynamically based on attribute names, instead of manual bookkeeping 2010-05-10 20:58:01 +10:00
Jeremy Ashkenas
9a7420ccd0 adding support for calling variadic functions with less than the requested number of arguments. 2010-05-08 12:44:54 -04:00
Jeremy Ashkenas
be72120311 further minor cleanup to varargs 2010-05-08 12:20:14 -04:00
Jeremy Ashkenas
c452c3a101 minor cleanup to varargs 2010-05-08 12:15:47 -04:00
Tim Jones
0b3bb66708 Added safe soaking on non-existent variables. 2010-05-05 21:58:48 +12:00
Jeremy Ashkenas
d0d0fa4d10 disallowing regex literals as implicit calls immediatly after ']', a rare case, I hope. Ticket #358 2010-05-04 23:50:22 -04:00
Jeremy Ashkenas
6222ed622f forcing spaces for implicit calls ... making '@ name' a call instead of an access. Ticket #353 2010-05-04 23:44:54 -04:00
Jeremy Ashkenas
1a03e98057 making 'while' and 'until' have the same associativity and precedence as 'for', #356 2010-05-04 23:37:03 -04:00
Jeremy Ashkenas
46cea93fc3 recompiling annotated documentation 2010-05-04 23:31:28 -04:00
Jeremy Ashkenas
d64b8fd9d8 merged in Trevor Burnham's recursive coffee compilation, with some adjustments... 2010-05-04 23:22:28 -04:00
Jeremy Ashkenas
c051daee2f Merge commit 'TrevorBurnham/master' 2010-05-04 23:01:15 -04:00
Tim Jones
ae70d10996 Removing obsolete delete calls. 2010-05-03 21:17:30 -04:00
Tim Jones
52e6399e02 Added missing or to IndexNode. 2010-05-03 21:17:23 -04:00
Tim Jones
ac05f62f2f Wrapping all soaked chains that are involved in operations. 2010-05-03 21:17:16 -04:00
Tim Jones
4d935efd09 Stop anonymous supers. 2010-05-03 21:16:50 -04:00
Trevor Burnham
fa8cc7976a Added recursive compilation and monitoring option to coffee command 2010-05-03 17:38:59 -04:00
Jeremy Ashkenas
ee4e34bf6d relative requires... 2010-05-01 11:00:43 -04:00
gfxmonk
c8e0f8b149 Cleaned up IfNodes
- renamed rewrite_condition() to switches_over(),
   and @switcher to @switch_subject
 - removed unused else_body constructor parameter, as well
   as unnecessary push() method
 - ensure both @body and @else_body are always Expressions
   (previously they could be either Expressions or IfNode)
2010-05-02 00:39:34 +10:00
gfxmonk
b47188763c add require statements to tests that depend on coffee-script modules 2010-05-02 00:27:50 +10:00
Jeremy Ashkenas
5e5c9df5c4 removing holmsand's cleanup 2010-05-01 08:40:02 -04:00
Jeremy Ashkenas
1a97f599dd added a test for the multiple-functions-passed-sans-parens 2010-04-30 23:20:22 -04:00
Tim Jones
3b264c9572 Added a small part to the rewriter to allow a better two-function call. 2010-05-01 14:04:57 +12:00
Dan Holmsand
144c096ae6 Remove unused variable index_var from for loops 2010-04-30 09:48:30 +02:00
Jeremy Ashkenas
adbcd320b2 adding until loops as the inverse of while loops 2010-04-28 22:08:00 -04:00
Jeremy Ashkenas
17ba44056e changing 'filter' to 'guard' to get around Express' clobbering of Object.prototype. 2010-04-28 21:46:47 -04:00
Jeremy Ashkenas
502abade7c rewriting the compiler using 'unless' blocks where appropriate. 2010-04-27 19:38:24 -04:00
Jeremy Ashkenas
b746c9018e adding 'unless' blocks 2010-04-27 19:35:15 -04:00
Jeremy Ashkenas
92af641827 no newline on no_such_task for Cake 2010-04-27 07:59:19 -04:00
Jeremy Ashkenas
b5606a247d adding pattern matching for comprehensions. 2010-04-26 23:46:35 -04:00
Jeremy Ashkenas
d62baf5a5d Merge branch 'for_destructuring' of git://github.com/StanAngeloff/coffee-script 2010-04-26 23:34:07 -04:00
Jeremy Ashkenas
e17567866a Merge branch 'master' of git://github.com/Tesco/coffee-script 2010-04-26 23:31:18 -04:00
Stan Angeloff
76f9596f22 Cleaning up. 2010-04-26 21:54:31 +03:00
Stan Angeloff
2a3a713811 Allowing pattern matching within for..loops 2010-04-26 21:35:35 +03:00
gfxmonk
175ebb3cd8 fail with appropriate error status if child process fails 2010-04-26 16:08:19 +10:00
Tim Jones
95367a4a63 Fixing silly mistake with the comments. 2010-04-26 16:00:12 +12:00
Tim Jones
8950c3c4c8 Fixing invalid pattern matching and object creation. 2010-04-26 15:54:47 +12:00
Jeremy Ashkenas
2d1abd099d rewriting the Lexer, CommandLine, Nodes, and Rewriter to take advantage of the new DRY object pattern matching. 2010-04-25 22:29:43 -04:00
Jeremy Ashkenas
a894db35fd Got the DRY object pattern matching style working properly, from Harmony. {name, age}: person now works correctly. 2010-04-25 22:21:53 -04:00
Jeremy Ashkenas
328a14014c eliminating the IndentedAssignList nonterminal. 2010-04-25 21:22:29 -04:00
Jeremy Ashkenas
315a2c63fa simplifying the grammar by replacing all of our trailing comma rules with an OptComma nonterminal. 2010-04-25 21:17:46 -04:00
Jeremy Ashkenas
ca4ea7649d Throwing an error when pattern matching has a non-identifier on the left-hand side. 2010-04-25 11:22:15 -04:00
Jeremy Ashkenas
08c877ec7b Fixing string keys in pattern matching on objects (ticket 325) 2010-04-25 11:07:09 -04:00
Jeremy Ashkenas
212ad45be4 merging gfxmonk's removed error event fix. 2010-04-24 22:13:13 -04:00
gfxmonk
e9b37c7578 replaced nonexistant error event callback with a combination of stderr and exit events 2010-04-25 11:24:06 +10:00
Jeremy Ashkenas
1438cecfad Fixing splats-with-super()-in-classes, an oversight. 2010-04-24 15:57:15 -04:00
Jeremy Ashkenas
49824ce1a6 removing yytext mentions for real this time. 2010-04-21 23:21:48 -04:00
Jeremy Ashkenas
bc0ec9dc07 Revert "removing yytext mentions from the grammar"
This reverts commit 5957b9f155.
2010-04-21 23:10:45 -04:00
Jeremy Ashkenas
5957b9f155 removing yytext mentions from the grammar 2010-04-21 23:01:14 -04:00
Jeremy Ashkenas
72e6e828f1 new version of Jison vendored ... rebuild the grammar, tests pass. 2010-04-21 22:52:20 -04:00
Jeremy Ashkenas
01b4393fa7 fixing Issue #328, parse error with indents and comments (StanAngeloff) 2010-04-21 22:26:45 -04:00
Jeremy Ashkenas
bf2a5386f9 requiring helpers module in test_bind.coffee 2010-04-21 19:52:58 -04:00
Jeremy Ashkenas
fb4d4a609b making bin/cake install add CoffeeScript as a Node.js library, and creating an index.js file for the top-level inclusion. 2010-04-20 20:20:38 -04:00
Jeremy Ashkenas
1cddb2aa88 Redoing Rewriter#add_implicit_parentheses to be more robust. 2010-04-20 01:32:12 -04:00
Jeremy Ashkenas
aac9679282 merging in gfodor's excellent Rewriter patch. 2010-04-19 23:18:39 -04:00
Jeremy Ashkenas
90472685e8 Merge branch 'master' of git://github.com/gfodor/coffee-script 2010-04-19 23:12:24 -04:00
Jeremy Ashkenas
7bb764b3e4 ignoring raw 2010-04-18 18:14:17 -04:00
Greg Fodor
1aed9c545f Fix for bug with chaining 2010-04-18 00:41:47 -04:00
Jeremy Ashkenas
c937e49689 adding 'coffeescript-idea' to the resources section. 2010-04-13 23:41:18 -04:00
Jeremy Ashkenas
0d860516ac fixing non-spaced function application. 2010-04-13 09:05:55 -04:00
Jeremy Ashkenas
4ced1d65b4 allowing 'debugger' ... it should never have been a reserved word. 2010-04-12 21:23:01 -04:00
Jeremy Ashkenas
92540d5e85 CoffeeScript 0.6.1, for Node.js v0.1.90 2010-04-12 21:20:00 -04:00
Jeremy Ashkenas
ea982a627e adding coffee-haml-filter to the docs 2010-04-11 17:57:29 -04:00
Jeremy Ashkenas
835ecac8db simplifying some unecessary interpolated expressions into interpolated values. 2010-04-11 16:57:53 -04:00
Jeremy Ashkenas
c3bbb48041 adding a test case for issue 309, interpolations with implicit calls. 2010-04-11 09:37:48 -04:00
Jeremy Ashkenas
2e842f0146 merging Stan's recursive tokenizing fix for interpolations. 2010-04-11 09:26:21 -04:00
Jeremy Ashkenas
8de2fb9009 Merge commit 'StanAngeloff/issue_309' 2010-04-11 09:22:31 -04:00
Stan Angeloff
17e177405a FIXES #309: Optional parens and interpolation. 2010-04-11 12:22:54 +03:00
Dr Nic Williams
ef67561fb3 Using anonymous callback for 'exit' event when running tests 2010-04-11 19:05:49 +10:00
Jeremy Ashkenas
df97effb9c fixing implicit-call-in-function-in-parens bug. 2010-04-10 18:56:46 -04:00
Jeremy Ashkenas
8317960f81 Battery of patches for compatibility with Node v0.1.90 2010-04-10 18:05:35 -04:00
Jeremy Ashkenas
065bf54094 generated closures should only call() or apply() when necessary. 2010-04-10 14:40:05 -04:00
Jeremy Ashkenas
f36acc27e5 safely preserving the arguments object through generated closure wrappers. 2010-04-10 14:20:32 -04:00
Jeremy Ashkenas
491ad6de95 adding webchat to the doc page 2010-04-10 13:42:39 -04:00
Jeremy Ashkenas
3eedf8ed1b Adding noonat to the contributor list 2010-04-06 21:09:23 -04:00
Jeremy Ashkenas
75b260e495 Merge branch 'master' of git://github.com/noonat/coffee-script 2010-04-06 21:08:29 -04:00
noonat
2351948291 Renamed Lexer.tag() argument to new_tag, due to Rhino scope confusion 2010-04-05 22:58:56 -07:00
Tim Jones
538e518d76 Realigning function arrows. 2010-04-06 02:29:38 +12:00
Tim Jones
de955dacc4 Added Statement to the grammar. 2010-04-06 02:26:23 +12:00
Chris Lloyd
19ed63129e Interpolated strings are expressions. 2010-04-04 17:05:52 +10:00
Chris Lloyd
2e744a1c1b Failing test for string interpolation. Interpolated strings need to be expressions, not values. 2010-04-04 16:54:59 +10:00
Jeremy Ashkenas
89534b88b1 merging in Chris Lloyd's fix for optional descriptions on Cake tasks. 2010-04-04 00:20:17 -04:00
Jeremy Ashkenas
c067808b54 CoffeeScript 0.6.0 is on the books. 2010-04-03 20:43:50 -04:00
Jeremy Ashkenas
59ae79d8fb rebuilding the docs with the fixed highlighter for '@' and regexes. 2010-04-03 19:10:26 -04:00
Jeremy Ashkenas
ad1c5e1884 merging non-func-constructor-fix, but a little more forgiving. 2010-04-03 14:53:26 -04:00
Stan Angeloff
9958cedd89 Throwing proper error when "constructor" is not a function within a class body. 2010-04-03 21:43:42 +03:00
Jeremy Ashkenas
d1aaed4430 Merge branch 'master' of git://github.com/Tesco/coffee-script 2010-04-03 12:21:31 -04:00
Tim Jones
89debc87b2 Removing {@prop: value} from the grammar. 2010-04-04 04:18:29 +12:00
Jeremy Ashkenas
9d2c81ea54 rebuilding the docs with new pygments, no errors on '@' 2010-04-03 12:01:46 -04:00
Jeremy Ashkenas
44765907b3 tiny tweak 2010-04-03 11:16:49 -04:00
Jeremy Ashkenas
4a85f3d499 cleaning up tests ... consolidation, consistency... 2010-04-03 10:39:32 -04:00
Jeremy Ashkenas
f99b5ad463 Merging Tesco's invoking-this-fix 2010-04-03 09:58:45 -04:00
Tim Jones
8fc631269b Added a rule to prevent invoking THIS. 2010-04-03 14:14:16 +13:00
Tim Jones
a1975583a7 Added THIS to CALLABLE. 2010-04-03 13:58:21 +13:00
Jeremy Ashkenas
1c628e7883 fixing parens-around-implicit-function-with-multiline-chained-chaser bug 2010-04-01 23:38:20 -04:00
Jeremy Ashkenas
3605168e85 fixing single evaluation of functions used in chained comparisons wrapped in parentheses. 2010-03-31 22:48:47 -04:00
Jeremy Ashkenas
f86fca2739 merged tanob's fix for installing outside of /usr/local without a /bin 2010-03-31 21:30:14 -04:00
Adriano Bonat
0410748e2d Merge remote branch 'upstream/master' 2010-03-31 22:14:26 -03:00
Adriano Bonat
2172878f21 When installing in different prefixes, prefix + '/bin' may not exist. 2010-03-31 09:38:05 -03:00
Jeremy Ashkenas
d3a51fbfa1 stylistics: removing a bunch of unecessary parentheses 2010-03-31 00:17:49 -04:00
Jeremy Ashkenas
aae2405de4 removing all of the 'type' tags from the Nodes. Simply using constructor.name instead. 2010-03-31 00:04:14 -04:00
Jeremy Ashkenas
2b578367a9 rebuilding the source documentation with the new utility refactors 2010-03-30 23:53:02 -04:00
Jeremy Ashkenas
f9b028b78c __extend back to __extends, as is the correct name. 2010-03-30 20:15:51 -04:00
Jeremy Ashkenas
572aa4e98f reverting the grammar to the pre-slice notation. 2010-03-30 20:11:40 -04:00
Jeremy Ashkenas
864275f07e removing __range, and all the slice behavior it enabled. If you can't do array[-1], then you shouldn't be able to do array[0..-1] -- it's just too inconsistent. 2010-03-30 20:06:44 -04:00
Jeremy Ashkenas
998a7c8cb0 more cleanups, added a utility helper function to the codegen 2010-03-30 19:48:37 -04:00
Jeremy Ashkenas
6d7a04228f another reshuffle ... removed utilities.coffee entirely. 2010-03-30 19:42:09 -04:00
Jeremy Ashkenas
4a8c2e8a13 more refactors to utilities ... removing dependencies and the namespacing 2010-03-30 19:27:38 -04:00
Jeremy Ashkenas
f3a60edc5d simplifying the lookup of the top-level scope object 2010-03-30 19:21:14 -04:00
Jeremy Ashkenas
1e1146d61d more utility simplifications 2010-03-30 19:17:40 -04:00
Jeremy Ashkenas
832e1d8cb8 Utilities doesn't need to be a class, and removing __utilities.keys 2010-03-30 19:00:59 -04:00
Jeremy Ashkenas
4936211a9c modified shorter imlementation of bind 2010-03-30 18:49:55 -04:00
Jeremy Ashkenas
f0d731009f merged matehat's utility branch, arraySlice -> slice, removed dead dependency... 2010-03-30 18:27:53 -04:00
Jeremy Ashkenas
a6248d03e5 Merge branch 'slice' of git://github.com/matehat/coffee-script 2010-03-30 18:19:41 -04:00
Jeremy Ashkenas
a934cf4947 make JSLint happy about the while condition 2010-03-30 18:19:09 -04:00
matehat
c498b7090e Removed __splice in the same manner 2010-03-30 18:14:51 -04:00
matehat
ca9e45e8af Removed the __slice method, in favor of the native array slice method 2010-03-30 17:57:23 -04:00
matehat
97096696a2 Put back every utility functions on the global scope, automatically prefixed with __ and set them dynamically as reserved on the lexer. 2010-03-30 16:48:43 -04:00
matehat
27fb3763b4 A set of improvements on previous code 2010-03-30 16:14:07 -04:00
matehat
da43c70488 Merged in StanAngeloff excellent slice branch, applying recent factoring of utility functions 2010-03-30 15:43:30 -04:00
Stan Angeloff
76ade0cb4d Removing vendor specific files for measurement tests. 2010-03-30 14:33:57 -04:00
Stan Angeloff
09e1526bca Removing commented code in compile_splice -- this is working correctly now. 2010-03-30 14:33:57 -04:00
Stan Angeloff
7d1fbeb708 Re-compiling the core using the new __slice and __splice functions. 2010-03-30 14:32:37 -04:00
Stan Angeloff
15217c705e Allowing for negative indices in slice literals. 2010-03-30 14:30:15 -04:00
matehat
9f108e87eb Removed unused __hasProp assignment and declared Coffeescript a reserved name 2010-03-30 11:20:53 -04:00
matehat
1e786d6d8b Removed unnecessary check 2010-03-30 10:58:21 -04:00
matehat
0557eb9b93 Removed hard-coding of utility object name 2010-03-30 09:08:16 -04:00
matehat
241f6f3068 Applied the utility factoring into a "Coffeescript" object to the core. All tests pass fast. 2010-03-30 09:02:51 -04:00
Jeremy Ashkenas
326656245a using the new static properties of class definitions in the CoffeeScript compiler -- it's hardly used. 2010-03-29 21:49:20 -04:00
Jeremy Ashkenas
177ec92c39 adding class methods to class definition syntax, using '@' 2010-03-29 21:43:12 -04:00
Jeremy Ashkenas
711dacae5f more little documentation for the rewriter ... moving along 2010-03-29 21:22:12 -04:00
Jeremy Ashkenas
c19183118e removing a case from Rewriter#add_implicit_parentheses that apparently never happens... 2010-03-29 20:52:22 -04:00
Jeremy Ashkenas
0af132d0c6 update internal documentation ... it's been a while 2010-03-29 20:48:41 -04:00
Jeremy Ashkenas
83c0e77ca8 making the Rewriter's add_implicit_calls more sensitive of parenthetical arguments. 2010-03-28 17:12:30 -04:00
Jeremy Ashkenas
1e315b5a33 fixing single-line implicit call wrapped around function with trailing arguments 2010-03-28 16:44:41 -04:00
Tim Jones
6e0e0767f9 Removed unprocessed values on a for loop from the grammar. 2010-03-29 07:32:01 +13:00
Tim Jones
6df50399a9 Restricted class and extends values to simple assignments. 2010-03-29 06:14:35 +13:00
Tim Jones
7b9a8998cf Addressing some assignment issues. 2010-03-29 06:06:16 +13:00
Jeremy Ashkenas
7de5253318 removing unused reserved variable 'source var' from range comprehensions 2010-03-27 16:04:47 -04:00
Jeremy Ashkenas
eaf4a71d32 Revert "Added Unix-like piping. Allows chaining of function calls where every succeeding call receives as first argument the result of all preceding expressions."
This reverts commit 7ee10e06be.
2010-03-27 15:25:34 -04:00
Jeremy Ashkenas
4dd40034ed Revert "removing the special case for | or"
This reverts commit 9763839ed1.
2010-03-27 15:25:27 -04:00
Jeremy Ashkenas
030476d335 Revert "typo for @compile_bitwise_or"
This reverts commit 45aae5e322.
2010-03-27 15:25:19 -04:00
Jeremy Ashkenas
45aae5e322 typo for @compile_bitwise_or 2010-03-27 12:13:24 -04:00
Jeremy Ashkenas
9763839ed1 removing the special case for | or 2010-03-27 10:28:08 -04:00
Stan Angeloff
7ee10e06be Added Unix-like piping. Allows chaining of function calls where every succeeding call receives as first argument the result of all preceding expressions. 2010-03-27 15:49:33 +02:00
matehat
8f3ea1d0c5 Fixed a small bug that happened when having a trailing comma in multiline array and object literals 2010-03-26 14:11:34 -04:00
matehat
b9b87f7d8e Minor modifications to the grammar to allow a single trailing comma for function call arguments, array literal and object literals. Adjusted tests accordingly 2010-03-26 11:44:25 -04:00
Chris Lloyd
c9b6e82e2c Optional sentence descriptions for cake tasks. 2010-03-26 20:25:17 +11:00
Jeremy Ashkenas
c8f969b4a2 adding a test case for the explicit returns 2010-03-25 18:54:17 -04:00
Jeremy Ashkenas
ecd1c77f48 fixing explicit returns of comprehensions (and probably other things as well) 2010-03-25 18:51:24 -04:00
Jeremy Ashkenas
ad93d2fe4d added another language extension test for a << b into a.push(b) 2010-03-23 20:42:40 -04:00
Jeremy Ashkenas
5a4d401582 make sure that the source-hacking docs mention to 'git checkout lib' 2010-03-23 00:25:37 -04:00
Jeremy Ashkenas
590c069158 CoffeeScript 0.5.6 2010-03-23 00:18:50 -04:00
Jeremy Ashkenas
94185e3f70 Merge branch 'master' of git://github.com/matehat/coffee-script 2010-03-22 23:20:05 -04:00
Jeremy Ashkenas
9adcd16ec6 '=' -> ':' 2010-03-22 18:48:41 -04:00
matehat
74bcd898e7 improving readability a bit 2010-03-22 02:02:04 -04:00
matehat
296808d4d3 Added splats positional flexibility to pattern matching 2010-03-22 01:52:47 -04:00
Jeremy Ashkenas
b7519cb834 switching a couple of the half_assignment tests to ensure that : and = are still interchangeable 2010-03-21 23:46:58 -04:00
Jeremy Ashkenas
7d6c050048 more half-assignment tests 2010-03-21 23:39:05 -04:00
Jeremy Ashkenas
16f9a2e6b7 rewriting the compiler to use half-expression assignment 2010-03-21 23:33:24 -04:00
Jeremy Ashkenas
cbfe7f5822 fix and tests for half assignments... whew. 2010-03-21 23:24:24 -04:00
Jeremy Ashkenas
895cd88761 almost there with half-assignments 2010-03-21 23:06:04 -04:00
Jeremy Ashkenas
f2ea18b0ba removing AND OR NOT IS ISNT from the grammar 2010-03-21 22:07:46 -04:00
Jeremy Ashkenas
162f6b9d5c one step at a time 2010-03-21 22:05:49 -04:00
Jeremy Ashkenas
4f8ae3ccbe one more step 2010-03-21 21:46:53 -04:00
Jeremy Ashkenas
acd9be3863 one step further 2010-03-21 21:46:06 -04:00
Jeremy Ashkenas
ddf18ae34c nothing much 2010-03-21 21:26:12 -04:00
Jeremy Ashkenas
0da61ec47e adding a couple of interchangeable tests 2010-03-21 21:07:32 -04:00
Jeremy Ashkenas
80230414a2 merging in gfxmonk's major refactor to the way that returns are pushed down into the interior of expressions 2010-03-21 11:28:05 -04:00
Jeremy Ashkenas
ce7c0d176b briefer test runner output 2010-03-21 10:18:23 -04:00
Jeremy Ashkenas
4ec79732f1 merging gfxmonk's cleanups 2010-03-21 10:11:02 -04:00
gfxmonk
cc3c314988 Cleaned up return logic
- ReturnNodes are explicitly added during compilation
 - ReturnNode is used instead of scattering "return" throughout
   code compilation snippets
 - nodes gain a make_return method in order to do the most useful
   thing when a return is requested
2010-03-21 22:21:55 +11:00
gfxmonk
8553a89af2 improve feedback when running the test task
- run synchronously, to ensure ordering is consistent
 - big red error message (and exit status)
 - don't bail early
 - don't bother printing a stack trace (it's useless
   until we can get line numbers in eval'd code)
2010-03-21 22:21:47 +11:00
Jeremy Ashkenas
566321d67a fixing infinite recursion when compiling if statements containing pure_statements 2010-03-20 10:36:06 -04:00
Jeremy Ashkenas
a2778bf06d resolving issues with comments in the middle of unfinished single-line expressions. 2010-03-20 00:58:25 -04:00
Jeremy Ashkenas
29eff23490 renaming test_curry to test_bind 2010-03-19 23:32:00 -04:00
Jeremy Ashkenas
4ebaa82563 removing the CoffeeScript.run named function from eval'd script scope by making it anonymous 2010-03-19 23:15:42 -04:00
Jeremy Ashkenas
69911209ea adding a pattern-matching-on-expression test 2010-03-19 22:48:55 -04:00
Jeremy Ashkenas
92688c89ef merging in matehat's nice refactor to splats (in arrays and otherwise) 2010-03-19 22:41:23 -04:00
matehat
fe04f8ce6b Added function call's flexibility with splats to array literals, factoring out splat compiling, and adjusted tests 2010-03-18 09:31:40 -04:00
Jeremy Ashkenas
b72641693d fixing doubled semis 2010-03-18 08:45:26 -04:00
Jeremy Ashkenas
0c6ee52cfc merging in matehat's curry branch. cleaning some loose ends up. adding some tests. 2010-03-18 00:42:26 -04:00
matehat
1f87094628 Slightly altered syntax, similar to Underscore's Function#bind form, highly simplified lexing and parsing and no polluted scope. Passing tests included. 2010-03-18 00:19:32 -04:00
matehat
3b22018296 Applied the new splat flexibility to function currying operator 2010-03-18 00:08:34 -04:00
matehat
5be437deb9 Added some inline docs 2010-03-18 00:08:34 -04:00
matehat
8d098194dd Added the '<-' currying operator, as heavily discussed in #251, along with tests. 2010-03-18 00:08:34 -04:00
matehat
256525bfa2 Fixed a small bug about flexible splats that occured when the leading array was a literal 2010-03-18 00:08:05 -04:00
Jeremy Ashkenas
79e4f30f69 fixing a bug where the Lexer assumed that values would always be strings. 2010-03-17 22:16:18 -04:00
Jeremy Ashkenas
884637468e fixing off-by-one error in compile_splat_arguments 2010-03-17 22:07:11 -04:00
Jeremy Ashkenas
6fd7810d89 can't use indexing[] into strings cross-browser, use substr() instead. 2010-03-17 21:41:00 -04:00
Jeremy Ashkenas
2f97d0d9b1 merging matehat's flexible splats (at any position within argument lists) 2010-03-17 21:15:54 -04:00
Jeremy Ashkenas
1bb9c57767 Merge branch 'splats' of git://github.com/matehat/coffee-script 2010-03-17 21:07:31 -04:00
Jeremy Ashkenas
d880b8b8f2 adding interpolation to heredocs, using the same rules as for strings 2010-03-17 20:47:27 -04:00
matehat
7129f518a4 Added the ability for function declaration to have a splat at an arbitrary position, not just at the end. Still restrict their number to 1. Adjusted tests accordingly. 2010-03-17 15:00:19 -04:00
matehat
fa6f1c2fb1 Fixed a tiny slicing bug 2010-03-17 15:00:19 -04:00
matehat
2bd1c3acca Added the ability to put as many splats in a function call as one wants. Also optimized the assembly into static arrays. Adjusted tests accordingly. 2010-03-17 15:00:19 -04:00
Jeremy Ashkenas
70cfc9500e pushing docs to the site 2010-03-16 23:18:54 -04:00
Jeremy Ashkenas
61b011cc28 fixing trailing whitespace on the final line 2010-03-16 19:57:39 -04:00
Jeremy Ashkenas
391135b1a5 fixing the relative path to 'grammar' 2010-03-16 19:36:08 -04:00
Jeremy Ashkenas
12d8d70573 fixing incorrectly-rooted relative paths in bin/cake and bin/coffee 2010-03-16 19:27:11 -04:00
Jeremy Ashkenas
04c5aeda2d Merge branch 'master' of git://github.com/matehat/coffee-script 2010-03-16 19:15:21 -04:00
Jeremy Ashkenas
25438be207 new docs for underscore 2010-03-16 19:13:13 -04:00
matehat
33f51d76f4 Added the possibility to end an identifier with '::', refering directly to its prototype. 2010-03-16 17:39:36 -04:00
Jeremy Ashkenas
e3021909c2 comments and tests 2010-03-16 06:54:49 -04:00
Jeremy Ashkenas
61dee1beba tweakin' 2010-03-15 23:10:14 -07:00
Jeremy Ashkenas
21e954eec3 updating module loading test 2010-03-15 23:08:58 -07:00
Jeremy Ashkenas
0b3054a348 merging in matehat's registerExtension patch for direct-requires of CoffeeScript. 2010-03-15 23:03:30 -07:00
Jeremy Ashkenas
02f4cb75dd removing deprecated references to process.mixin in favor of helpers.extend for Node 0.1.32 2010-03-15 22:53:25 -07:00
Jeremy Ashkenas
3aeb8c6bdb moving the contains-a-pure-statement-means-no-closure logic into ClosureNode.wrap itself. 2010-03-15 22:27:31 -07:00
Jeremy Ashkenas
8c2b2c7f80 merging in the fixed branch for gfxmonk/break_issue 2010-03-15 22:22:41 -07:00
Jeremy Ashkenas
745c635a55 fixing issue with break statements and for loops and closures and the value of 'this' 2010-03-15 22:20:29 -07:00
Jeremy Ashkenas
c7dd31846d merging in gfxmonk's fix for reporting line numbers for unclosed { [ ( opening symbols 2010-03-15 22:09:16 -07:00
gfxmonk
e87fa4293d report line number of opening token if a pair (eg bracket) is left open 2010-03-15 22:07:40 -07:00
gfxmonk
b269884f8d fixed non-recursive traverse() for CodeNode 2010-03-15 22:03:54 -07:00
Jeremy Ashkenas
3fed9761a6 let's make that non-greedy 2010-03-15 21:50:33 -07:00
Jeremy Ashkenas
6ccf196b61 adding another language extension test, with %w{} style ruby word array literals 2010-03-15 21:47:40 -07:00
Jeremy Ashkenas
a4bd8dc623 removing outdated arguments tests 2010-03-15 21:45:37 -07:00
Jeremy Ashkenas
34f01408c9 removing arguments-to-arrays from the docs 2010-03-15 20:53:43 -07:00
Jeremy Ashkenas
73074daa07 removing arguments-to-array-conversion from coffee 2010-03-15 20:46:14 -07:00
Jeremy Ashkenas
119b80d449 removing fiddling with require.paths from CoffeeScript 2010-03-15 20:39:46 -07:00
Jeremy Ashkenas
60b3103314 using granular helper imports, for nicer within. 2010-03-14 14:58:32 -07:00
Jeremy Ashkenas
75ffb9dc84 stop mixing in helpers (t(ticket #250) -- with a test 2010-03-14 14:48:43 -07:00
Jeremy Ashkenas
b306d40bf1 we don't need relative requires for the language 2010-03-14 13:48:58 -07:00
Jeremy Ashkenas
a27f146338 fixing 'cake test' to print out the name of the file with the failing test. 2010-03-14 09:33:41 -07:00
Jeremy Ashkenas
74995358cd updating the Cakefile for the new tmbundle location. 2010-03-14 08:59:55 -07:00
Jeremy Ashkenas
35da6c32b7 removing the tmbundle from the main repository. See jashkenas/coffee-script-tmbundle for the new one 2010-03-14 08:05:33 -07:00
Jeremy Ashkenas
701a5c7cb9 Merge branch 'master' of git://github.com/drnic/coffee-script 2010-03-14 07:39:02 -07:00
gfxmonk
3c3e7f666b added another "invalid break" example, with code not on the top-level 2010-03-14 13:02:36 +11:00
gfxmonk
89dfa1fd7a added code to illustrate an issue with break statements 2010-03-14 13:02:35 +11:00
gfxmonk
1da00f0ee3 added a test case for broken "break" statement 2010-03-14 11:35:56 +11:00
gfxmonk
5809a1637f fixed non-recursive traverse() for CodeNode 2010-03-14 00:05:51 +11:00
gfxmonk
e47bea34d5 report line number of opening token if a pair (eg bracket) is left open 2010-03-14 00:03:17 +11:00
matehat
709f17c278 Added an extension on node's 'require' module so it can import .coffee module and added some tests to make sure it works. 2010-03-12 14:22:01 -05:00
Jeremy Ashkenas
3c597dde72 no need to save the lookups 2010-03-10 22:32:00 -05:00
Jeremy Ashkenas
0379431812 tweaks 2010-03-10 21:53:33 -05:00
Jeremy Ashkenas
3f9fd85afb removing the unused CallNode#push 2010-03-10 20:53:31 -05:00
Jeremy Ashkenas
7b0a235c71 removing the nearly-unused notion of 'operation sensitive' 2010-03-10 20:19:32 -05:00
Jeremy Ashkenas
55cac23976 fixing indentation for prefix while nodes 2010-03-10 20:15:12 -05:00
Dr Nic Williams
22a16a3e33 added two snippets for creating functions; matching tab/key triggers from JavaScript bundle 2010-03-11 10:27:25 +10:00
Dr Nic Williams
db555d12ea can set Grammar via Shift+Ctrl+Alt+C 2010-03-11 10:16:34 +10:00
Jeremy Ashkenas
b9f1390405 adding more comment tests -- this terminator/comment as-block business really seems to have opened them up a bit 2010-03-10 16:44:50 -05:00
Jeremy Ashkenas
f2d0aee656 added Stan's fix for try/finally without catch 2010-03-10 16:27:30 -05:00
Jeremy Ashkenas
2a47727641 allowing terminator/comment as a block, so that you can have comment-only blocks 2010-03-10 16:18:17 -05:00
Jeremy Ashkenas
ccb7f63b8a fixing exports for the browser 2010-03-10 09:47:02 -05:00
Jeremy Ashkenas
1f9bb6a1c4 fixing broken multi-line optional-paren blocks 2010-03-10 09:28:00 -05:00
Jeremy Ashkenas
21b5d2cac5 tweaks to the helper docs, if they're going to be published 2010-03-10 00:05:30 -05:00
Jeremy Ashkenas
56eeeebed1 updating the docco 2010-03-09 23:53:21 -05:00
Jeremy Ashkenas
54627f6807 CoffeeScript language extensions are now working. 2010-03-09 23:44:29 -05:00
Jeremy Ashkenas
5754d36fdd adding the broken test 2010-03-09 23:32:54 -05:00
Jeremy Ashkenas
3d14d362a8 first draft of CoffeeScript language extensions. 2010-03-09 23:04:16 -05:00
Jeremy Ashkenas
472e027463 fixing the nested-implicit-parens-on-a-single-line 2010-03-09 21:54:44 -05:00
Jeremy Ashkenas
b297510d2b big refactor -- pulled all helper functions into helpers.coffee to facilitate sharing. 2010-03-09 21:24:30 -05:00
Jeremy Ashkenas
4932d25540 making balanced_string accept an array of delimiters, in hope of using it in the Rewriter 2010-03-09 20:53:56 -05:00
Jeremy Ashkenas
b6c3b743f0 Merge branch 'tm_highlighting' of git://github.com/cehoffman/coffee-script 2010-03-09 19:45:52 -05:00
Jeremy Ashkenas
1b4edd0e37 scrubbing all carriage returns from CoffeeScript source before compiling for the benefit of Windows users. 2010-03-08 23:07:26 -05:00
Jeremy Ashkenas
dcb00b4fe8 minor refactors to balanced_string 2010-03-08 22:48:14 -05:00
Jeremy Ashkenas
b33b688373 fixing more bugs in balance/interpolate/regexp/string/etc 2010-03-08 22:25:06 -05:00
Jeremy Ashkenas
4b97b15c0c more refactors to interpolation 2010-03-08 21:55:06 -05:00
Chris Hoffman
93c0774036 Regex interpolation highlighting 2010-03-08 20:48:23 -06:00
Chris Hoffman
645885d72d Merge branch 'master' of git://github.com/jashkenas/coffee-script 2010-03-08 20:34:36 -06:00
Jeremy Ashkenas
b5af5f66fb more regexp and string interpolation tweaks 2010-03-08 21:34:10 -05:00
Chris Hoffman
fb04f8900a Merge branch 'master' of git://github.com/jashkenas/coffee-script 2010-03-08 20:12:40 -06:00
Jeremy Ashkenas
3396dce2bb waypoint -- going to try to clean up regex_token 2010-03-08 20:57:28 -05:00
Jeremy Ashkenas
3291bd2a4a removing the 'supress' option from the lexer, just look at the slash. 2010-03-08 20:07:19 -05:00
Jeremy Ashkenas
121f01c06f merged stan's interpolation_3, a couple of tweaks 2010-03-08 19:43:12 -05:00
Jeremy Ashkenas
6cac2d57ba Merge commit 'StanAngeloff/interpolation_3' 2010-03-08 19:36:09 -05:00
Jeremy Ashkenas
c04b43e047 adding documentation for var shadowing 2010-03-08 19:27:38 -05:00
Jeremy Ashkenas
ff20732dd7 Merge branch 'tm_highlighting' of git://github.com/cehoffman/coffee-script 2010-03-08 19:14:34 -05:00
Stan Angeloff
81af8f296e Regular expression interpolations; fixed bug in string interpolations when all tokens were identifiers. 2010-03-08 20:06:51 +02:00
Stan Angeloff
830d1fb42b Merge branch 'master' of git://github.com/jashkenas/coffee-script into interpolation_3 2010-03-08 16:43:41 +02:00
Jeremy Ashkenas
e19cb48d3d Merge branch 'master' of git://github.com/uiru/coffee-script 2010-03-08 09:30:25 -05:00
Chris Hoffman
f755580b11 Match regex highlighting to how coffee parses it 2010-03-08 08:18:37 -06:00
Jeremy Ashkenas
81955005b9 doc tweaks 2010-03-08 09:13:15 -05:00
Will
62a871773b Include an extra line process.mixin require 'sys' on the top to let the .js output run using node (v0.1.31) 2010-03-09 01:03:43 +11:00
Chris Hoffman
5e52e7f19e While loop can be a single line expressions so don't indent when it is 2010-03-08 08:02:23 -06:00
Chris Hoffman
4c66a6a642 Merge branch 'master' of git://github.com/jashkenas/coffee-script 2010-03-08 07:33:45 -06:00
Chris Hoffman
9e1deecf13 Merge branch 'tm_highlighting' 2010-03-08 07:33:02 -06:00
Chris Hoffman
1ce93ccceb Indent function in symbol list based on indention in source 2010-03-08 07:30:15 -06:00
Chris Hoffman
4b04f8bec1 Add auto indent settings for textmate 2010-03-08 07:28:56 -06:00
Jeremy Ashkenas
5528bc7b2d scratch 'as seen above' 2010-03-08 06:44:47 -05:00
Jeremy Ashkenas
912f3b19f7 little tweaks to the docs 2010-03-08 06:40:54 -05:00
Chris Hoffman
9e5e85780b Don't highlight fresh[key]: val situations 2010-03-08 05:37:51 -06:00
Chris Hoffman
10aa3b3ae3 Revert "Properly highlight fresh[key]: val situations" 2010-03-08 05:37:07 -06:00
Jeremy Ashkenas
bcf7b3f95b CoffeeScript 0.5.5, with string interpolation and internal documentation 2010-03-08 06:34:07 -05:00
Chris Hoffman
07f1c784a4 Properly highlight fresh[key]: val situations 2010-03-08 05:29:01 -06:00
Chris Hoffman
465994cff9 Clean up some misplaced files 2010-03-08 05:26:38 -06:00
Chris Hoffman
697ad6cbda Merge remote branch 'origin' 2010-03-08 05:15:17 -06:00
Jeremy Ashkenas
a840671015 Finishing off the docs for nodes.coffee -- almost ready to roll. 2010-03-08 05:13:05 -06:00
Jeremy Ashkenas
7f0ab8308d making ThrowNode not a pure_statement -- it can jump out of the closure just fine 2010-03-08 05:13:05 -06:00
Jeremy Ashkenas
270b9fde04 waypoint -- documented down to the ThrowNode 2010-03-08 05:13:04 -06:00
Jeremy Ashkenas
993c9899cb Fixing up command-line args for --stdio and --eval. Now makes more sense with --run by default. 2010-03-08 05:13:04 -06:00
Jeremy Ashkenas
c8246e95f1 broke the flag for --no-wrap, fixed. 2010-03-08 05:13:04 -06:00
Stan Angeloff
f08de1ed4c Empty expression interpolations evaluate as empty strings now. 2010-03-08 05:13:04 -06:00
Jeremy Ashkenas
90f2e0dbb9 Merge branch 'tm_highlighting' of git://github.com/cehoffman/coffee-script 2010-03-08 06:11:15 -05:00
Chris Hoffman
80a3bd8951 Add variable assignment highlighting for textmate 2010-03-08 05:10:08 -06:00
Jeremy Ashkenas
b0aec3cbe2 Finishing off the docs for nodes.coffee -- almost ready to roll. 2010-03-08 05:42:57 -05:00
Jeremy Ashkenas
5f5e0634dd making ThrowNode not a pure_statement -- it can jump out of the closure just fine 2010-03-08 05:19:48 -05:00
Jeremy Ashkenas
049e605016 changing from storage.type... to variable.assignment -- it was a little garish in most color schemes I tried. 2010-03-08 05:16:45 -05:00
Jeremy Ashkenas
4687c3e140 Merge branch 'tm_highlighting' of git://github.com/cehoffman/coffee-script 2010-03-08 04:59:10 -05:00
Jeremy Ashkenas
6d74e223be waypoint -- documented down to the ThrowNode 2010-03-08 04:58:39 -05:00
Chris Hoffman
221427a6b4 Make variable assignment highlighting work in more cases 2010-03-08 03:55:03 -06:00
Chris Hoffman
3bf892128d Make old style classes show up as a function and don't let $\d be highlighted as a number 2010-03-08 03:47:49 -06:00
Chris Hoffman
abd7f939f2 Don't show object creation instances in symbol list for textmate 2010-03-08 03:46:38 -06:00
Jeremy Ashkenas
299e9918b9 Fixing up command-line args for --stdio and --eval. Now makes more sense with --run by default. 2010-03-08 04:46:24 -05:00
Chris Hoffman
eea83ff613 Add comment starter preference for textmate 2010-03-08 03:46:05 -06:00
Jeremy Ashkenas
9eceab667c broke the flag for --no-wrap, fixed. 2010-03-08 04:28:26 -05:00
Stan Angeloff
026e7649d5 Empty expression interpolations evaluate as empty strings now. 2010-03-08 04:13:59 -05:00
Chris Hoffman
c807da7664 Identify functions for symbol lookup and correct lookbehind assertion 2010-03-08 02:39:38 -06:00
Chris Hoffman
dde5db621d Add some TextMate triggers for string interpolation 2010-03-08 02:06:57 -06:00
Chris Hoffman
6b95cb4ee0 Fix the naked .property highlighting in interpolation to match coffee parsing 2010-03-08 01:40:04 -06:00
Stan Angeloff
a5f69ef716 Empty expression interpolations evaluate as empty strings now. 2010-03-08 09:31:31 +02:00
Chris Hoffman
accccb5f39 Allow $ to be the first character in a variable name for highlighting in textmate 2010-03-08 01:20:43 -06:00
Chris Hoffman
f8ddccd7d0 Escape $ in regexes where it was meant as character and not end of line 2010-03-08 01:05:52 -06:00
Chris Hoffman
e06e882962 Update highlighting for class definitions in textmate 2010-03-08 00:59:21 -06:00
Chris Hoffman
c3f74ca4f1 Add string interpolation highlighting and Cakefile highlighting 2010-03-08 00:43:04 -06:00
Jeremy Ashkenas
532464f7ae waypoint -- docs halfway down through the OpNode 2010-03-07 22:47:34 -05:00
Jeremy Ashkenas
570fb013e2 test tweaks 2010-03-07 22:29:46 -05:00
Jeremy Ashkenas
71ace9d8d0 allowing naked interpolation of dotted properties. .property 2010-03-07 22:26:25 -05:00
Jeremy Ashkenas
1cf0326183 unifying the CoffeeScript.compile and CoffeeScript.run apis to be the same -- source code and options hash. 2010-03-07 22:17:45 -05:00
Jeremy Ashkenas
5b9ebd19d5 adding source file information to all coffeescript compiles 2010-03-07 22:08:24 -05:00
Jeremy Ashkenas
6ce869b3fb adding a note about the 'git co lib' - build twice shuffle to the docs -- there's been more than one question about it already. 2010-03-07 21:57:08 -05:00
Jeremy Ashkenas
06b50ecb98 unifying all of the server-side evaluation under CoffeeScript.run -- this means that __filename and __dirname and relative requires should work from all angles under Node.js 2010-03-07 21:49:08 -05:00
Jeremy Ashkenas
0bc7719572 merging in Zaach's Jison updates 2010-03-07 19:11:03 -05:00
Jeremy Ashkenas
9028f79a27 Merge branch 'master' of git://github.com/zaach/coffee-script 2010-03-07 19:09:20 -05:00
Jeremy Ashkenas
a3e1693a75 waypoint -- docc'd down to the SplatNode 2010-03-07 19:07:37 -05:00
Zachary Carter
060b3e23c1 Merge branch 'master' of git://github.com/jashkenas/coffee-script 2010-03-07 18:59:54 -05:00
Zachary Carter
612c238157 Rebuild parser, now with skinnier parse table. 2010-03-07 18:53:23 -05:00
Zachary Carter
8adbc75811 Update Jison for table optimizations. 2010-03-07 18:48:15 -05:00
Jeremy Ashkenas
7485e5482c mention rlwrap in the docs on --interactive 2010-03-07 18:19:41 -05:00
Jeremy Ashkenas
659a5436a5 adding coffee-mode and rack-coffee to the Resources section of the docs 2010-03-07 17:52:14 -05:00
Jeremy Ashkenas
c6d7a27848 waypoint on the documentation -- almost halfway through the nodes 2010-03-07 17:31:39 -05:00
Jeremy Ashkenas
094b198d5d first little bit of commenting the nodes.coffee -- with some slight refactors 2010-03-07 16:41:06 -05:00
Jeremy Ashkenas
22b97a3b54 documenting scope.coffee -- nodes.coffee is the next, and last up to bat. 2010-03-07 15:45:45 -05:00
Jeremy Ashkenas
a4f7a5e248 documenting and cleaning up the Rewriter 2010-03-07 14:41:52 -05:00
Jeremy Ashkenas
45d8cf163e updating to the latest docco 2010-03-07 13:49:34 -05:00
Jeremy Ashkenas
d46daa1d7c documenting optparse.coffee and repl.coffee 2010-03-07 13:41:15 -05:00
Jeremy Ashkenas
4906cf1aff cleaned and commented the lexer (again) interpolate_string() continues to shrink 2010-03-07 12:47:03 -05:00
Stan Angeloff
f74fae58e3 Rewritting lexer.coffee to accept nested string interpolations. 2010-03-07 11:42:52 -05:00
Jeremy Ashkenas
1602e0e823 adding complete documentation for the grammar 2010-03-07 11:41:56 -05:00
Jeremy Ashkenas
202ebf06ff documentation for command.coffee 2010-03-07 00:28:58 -05:00
Jeremy Ashkenas
3e3b71724d making equality left-associative so that our chaining works properly with it. 2010-03-06 23:48:06 -05:00
Jeremy Ashkenas
893fb98522 moving command_line.coffee -> command.coffee 2010-03-06 22:18:15 -05:00
Jeremy Ashkenas
e267226438 commenting coffee-script.coffee for documentation 2010-03-06 20:30:40 -05:00
Jeremy Ashkenas
62626b712b Commenting cake.coffee for Docco docs. 2010-03-06 20:18:50 -05:00
Jeremy Ashkenas
7bdf14b210 Override process.argv as well as process.ARGV for eval'd scripts 2010-03-06 20:09:08 -05:00
Jeremy Ashkenas
87420e6504 updating the Function Binding docs with an improved explanation. 2010-03-06 19:29:41 -05:00
Jeremy Ashkenas
c73a3ec356 updating webserver example for node API 2010-03-06 19:23:25 -05:00
Jeremy Ashkenas
2e23e6a2dc removing narwhal.coffee and narwhal.js, because I don't think that anyone was using them. 2010-03-06 19:05:21 -05:00
Jeremy Ashkenas
5b3ef78101 adding a doc:source Cake task to document CoffeeScript's internals 2010-03-06 19:02:31 -05:00
Jeremy Ashkenas
9b262d56a5 allowing relative requires of JavaScript from directly-eval'd CoffeeScript. 2010-03-06 17:40:13 -05:00
Jeremy Ashkenas
453b43992d fixing line numbers in errors printed prior to parsing 2010-03-06 16:42:40 -05:00
Jeremy Ashkenas
a5e3617015 Adding a starts() helper to avoid substring() calls for simple matches. 2010-03-06 16:24:06 -05:00
Jeremy Ashkenas
c4ad6d1ee6 minor cleanups to balanced_group -> balanced_token, removing optional escaper (unused), and using it to implement interpolated javascript. 2010-03-06 16:06:47 -05:00
Stan Angeloff
f9cde1b46d Improving performance by removing call to bound function for every character in a string. Added a third option to \delimited\ to allow custom escape sequences. 2010-03-06 15:53:37 -05:00
Jeremy Ashkenas
03bc897b39 updating CoffeeScript docs to require Node 0.1.31 + 2010-03-06 15:48:20 -05:00
Stan Angeloff
83fd84745d Rewriting string tokenizer; allowing nested double-quoted strings inside expression interpolations. 2010-03-06 15:21:30 -05:00
Jeremy Ashkenas
e977967eb5 implementing the CoffeeScript compiler using interpolation where appropriate. 2010-03-06 13:59:11 -05:00
Jeremy Ashkenas
15b00cb3ca implementing string interpolation using string interpolation 2010-03-06 13:32:43 -05:00
Jeremy Ashkenas
18e5f72a84 done converting nodes.coffee code generation to use string interpolation where appropriate 2010-03-06 00:23:54 -05:00
Jeremy Ashkenas
47682de0f0 adding a test for intermingled identifier/expression interpolations 2010-03-05 22:54:39 -05:00
Jeremy Ashkenas
b4ea43cbd0 waypoint -- starting to implement nodes.coffee with interpolations, and fixing/shortening/combining the lexer implementation to allow identifier interpolations to be interleaved with expression interps 2010-03-05 22:52:28 -05:00
Jeremy Ashkenas
08341286a3 adding back parentheses wrapper around interpolated expressions -- we need it 2010-03-05 21:12:13 -05:00
Jeremy Ashkenas
4c3b0b9a74 allowing @properties to be referenced in naked interpolations 2010-03-05 21:05:31 -05:00
Jeremy Ashkenas
d250e9e9cc some edits to the interpolation path 2010-03-05 20:42:36 -05:00
Stan Angeloff
75be5eed62 Test line commented by mistake, no functional changes 2010-03-05 19:50:28 -05:00
Stan Angeloff
e2f86678a4 Allowing expressions to be used inside strings; syntax is $\{...\} 2010-03-05 19:50:20 -05:00
Stan Angeloff
fe7d5dfd19 Added string interpolation for identifiers 2010-03-05 19:49:48 -05:00
Jeremy Ashkenas
965034e16e add proper spacing to optparse by default 2010-03-04 22:59:03 -05:00
197 changed files with 11340 additions and 10447 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
raw
presentation
test.coffee
parser.output

View File

@@ -1,22 +1,27 @@
fs: require 'fs'
coffee: require 'coffee-script'
fs: require 'fs'
{helpers}: require('./lib/helpers')
CoffeeScript: require './lib/coffee-script'
{spawn, exec}: require('child_process')
# Run a CoffeeScript through our node/coffee interpreter.
run: (args) ->
proc: process.createChildProcess 'bin/coffee', args
proc.addListener 'error', (err) -> if err then puts err
proc: spawn 'bin/coffee', args
proc.stderr.addListener 'data', (buffer) -> puts buffer.toString()
proc.addListener 'exit', (status) -> process.exit(1) if status != 0
option '-p', '--prefix [DIR]', 'set the installation prefix for `cake install`'
task 'install', 'install CoffeeScript into /usr/local (or --prefix)', (options) ->
base: options.prefix or '/usr/local'
lib: base + '/lib/coffee-script'
lib: "$base/lib/coffee-script"
bin: "$base/bin"
exec([
'mkdir -p ' + lib
'cp -rf bin lib LICENSE README package.json src vendor ' + lib
'ln -sf ' + lib + '/bin/coffee ' + base + '/bin/coffee'
'ln -sf ' + lib + '/bin/cake ' + base + '/bin/cake'
"mkdir -p $lib $bin"
"cp -rf bin lib LICENSE README package.json src vendor $lib"
"ln -sf $lib/bin/coffee $base/bin/coffee"
"ln -sf $lib/bin/cake $base/bin/cake"
"mkdir -p ~/.node_libraries"
"ln -sf $lib/lib ~/.node_libraries/coffee-script"
].join(' && '), (err, stdout, stderr) ->
if err then print stderr
)
@@ -28,46 +33,70 @@ task 'build', 'build the CoffeeScript language from source', ->
run ['-c', '-o', 'lib'].concat(files)
task 'build:full', 'rebuild the source twice, and run the tests', ->
exec 'bin/cake build && bin/cake build && bin/cake test', (err, stdout, stderr) ->
print stdout if stdout
print stderr if stderr
throw err if err
task 'build:parser', 'rebuild the Jison parser (run build first)', ->
require.paths.unshift 'vendor/jison/lib'
parser: require('grammar').parser
parser: require('./lib/grammar').parser
js: parser.generate()
parser_path: 'lib/parser.js'
fs.writeFile parser_path, js
task 'build:ultraviolet', 'build and install the Ultraviolet syntax highlighter', ->
exec 'plist2syntax extras/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage', (err) ->
exec 'plist2syntax ../coffee-script-tmbundle/Syntaxes/CoffeeScript.tmLanguage', (err) ->
throw err if err
exec 'sudo mv coffeescript.yaml /usr/local/lib/ruby/gems/1.8/gems/ultraviolet-0.10.2/syntax/coffeescript.syntax'
task 'build:underscore', 'rebuild the Underscore.coffee documentation page', ->
exec 'uv -s coffeescript -t idle -h examples/underscore.coffee > documentation/underscore.html'
task 'build:browser', 'rebuild the merged script for inclusion in the browser', ->
exec 'rake browser', (err) ->
throw err if err
task 'doc', 'watch and continually rebuild the documentation', ->
task 'doc:site', 'watch and continually rebuild the documentation for the website', ->
exec 'rake doc'
task 'doc:source', 'rebuild the internal documentation', ->
exec 'docco src/*.coffee && cp -rf docs documentation && rm -r docs', (err) ->
throw err if err
task 'doc:underscore', 'rebuild the Underscore.coffee documentation page', ->
exec 'docco examples/underscore.coffee && cp -rf docs documentation && rm -r docs', (err) ->
throw err if err
task 'test', 'run the CoffeeScript language test suite', ->
process.mixin require 'assert'
test_count: 0
start_time: new Date()
[original_ok, original_throws]: [ok, throws]
process.mixin {
ok: (args...) -> test_count += 1; original_ok(args...)
throws: (args...) -> test_count += 1; original_throws(args...)
helpers.extend global, require 'assert'
passed_tests: failed_tests: 0
start_time: new Date()
original_ok: ok
helpers.extend global, {
ok: (args...) -> passed_tests += 1; original_ok(args...)
CoffeeScript: CoffeeScript
}
red: '\033[0;31m'
green: '\033[0;32m'
reset: '\033[0m'
process.addListener 'exit', ->
time: ((new Date() - start_time) / 1000).toFixed(2)
puts '\033[0;32mpassed ' + test_count + ' tests in ' + time + ' seconds\033[0m'
message: "passed $passed_tests tests in $time seconds$reset"
puts(if failed_tests then "${red}failed $failed_tests and $message" else "$green$message")
fs.readdir 'test', (err, files) ->
for file in files
fs.readFile 'test/' + file, (err, source) ->
js: coffee.compile source
process.compile js, file
files.forEach (file) ->
return unless file.match(/\.coffee$/i)
source: path.join 'test', file
fs.readFile source, (err, code) ->
try
CoffeeScript.run code.toString(), {source: source}
catch err
failed_tests += 1
puts "${red}failed:${reset} $source"
puts err.stack

22
README
View File

@@ -39,4 +39,24 @@
The source repository:
git://github.com/jashkenas/coffee-script.git
Contributors:
Stan Angeloff (StanAngeloff)
Jeremy Ashkenas (jashkenas)
Zach Carter (zaach)
Tim Cuthbertson (gfxmonk)
Mathieu D'Amours (matehat)
Chris Hoffman (cehoffman)
Jason Huggins (hugs)
Timothy Jones (Tesco)
Chris Lloyd (chrislloyd)
Matt Lyon (mattly)
Jeff Olson (olsonjeffery)
Nathan Ostgard (noonat)
Samuel Reis (grgh)
Tom Robinson (tlrobinson)
Tim Smart (Tim-Smart)
Dr. Nic Williams (drnic)

View File

@@ -23,7 +23,7 @@ end
desc "Build the single concatenated and minified script for the browser"
task :browser do
sources = %w(rewriter.js lexer.js parser.js scope.js nodes.js coffee-script.js)
sources = %w(helpers.js rewriter.js lexer.js parser.js scope.js nodes.js coffee-script.js)
code = sources.map {|s| File.read('lib/' + s) }.join('')
code = YUI::JavaScriptCompressor.new.compress(code)
File.open('extras/coffee-script.js', 'w+') {|f| f.write(code) }

View File

@@ -1,9 +1,8 @@
#!/usr/bin/env node
process.mixin(require('sys'));
var path = require('path');
var fs = require('fs');
var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');
require.paths.unshift(lib);
require('cake').run();
require(lib + '/helpers').helpers.extend(global, require('sys'));
require(lib + '/cake').run();

View File

@@ -1,9 +1,8 @@
#!/usr/bin/env node
process.mixin(require('sys'));
var path = require('path');
var fs = require('fs');
var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');
require.paths.unshift(lib);
require('command_line').run();
require(lib + '/helpers').helpers.extend(global, require('sys'));
require(lib + '/command').run();

View File

@@ -1,4 +0,0 @@
backwards: ->
alert arguments.reverse()
backwards "stairway", "to", "heaven"

View File

@@ -0,0 +1,5 @@
url: "documentation/coffee/binding.coffee"
get_source: jQuery.get <- jQuery, url
get_source (response) -> alert response

View File

@@ -1,5 +1,3 @@
process.mixin require 'assert'
task 'test', 'run each of the unit tests', ->
for test in test_files
fs.readFile test, (err, code) -> eval coffee.compile code

View File

@@ -6,4 +6,4 @@ if happy and knows_it
date: if friday then sue else jill
expensive ||= do_the_math()
expensive: or do_the_math()

View File

@@ -0,0 +1,2 @@
author: "Wittgenstein"
quote: "A picture is a fact. -- $author"

View File

@@ -0,0 +1,6 @@
sentence: "${ 22 / 7 } is a decent approximation of π"
sep: "[.\\/\\- ]"
dates: /\d+$sep\d+$sep\d+/g

View File

@@ -10,4 +10,4 @@ futurists: {
}
}
{poet: {name: poet, address: [street, city]}}: futurists
{poet: {name, address: [street, city]}}: futurists

View File

@@ -0,0 +1,7 @@
tag: "<impossible>"
[open, contents..., close]: tag.split("")

View File

@@ -1,7 +1,7 @@
# Econ 101
if this.studying_economics
while supply > demand then buy()
while supply < demand then sell()
buy() while supply > demand
sell() until supply > demand
# Nursery Rhyme
num: 6

View File

@@ -8,7 +8,7 @@ div.container {
width: 950px;
margin: 100px 0 50px 50px;
}
p {
p, li {
width: 625px;
}
a {
@@ -105,6 +105,7 @@ div.code {
position: fixed;
z-index: 100;
height: 50px;
min-width: 490px;
left: 40px; right: 40px; top: 25px;
background: #ddd;
padding-left: 235px;
@@ -124,7 +125,7 @@ div.code {
#error {
position: absolute;
-webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px;
right: 15px; top: 15px; left: 565px;
right: 15px; top: 15px; left: 722px;
height: 15px;
padding: 2px 5px;
background: #fdcdcc;
@@ -142,14 +143,14 @@ div.code {
float: left;
padding: 0 20px;
border: 1px solid #bbb;
border-top: 0; border-bottom: 0;
border-top: 0; border-bottom: 0; border-left-width: 0;
cursor: pointer;
}
body.full_screen .navigation {
position: static;
}
.navigation.try {
border-left: 0;
.navigation.toc {
border-left-width: 1px;
}
.navigation:hover,
.navigation.active {
@@ -206,7 +207,7 @@ div.code {
}
.navigation .contents a {
display: block;
width: 300px;
width: 290px;
text-transform: none;
text-decoration: none;
font-weight: normal;

View File

@@ -0,0 +1,45 @@
<!DOCTYPE html> <html> <head> <title>cake.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> cake.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p><code>cake</code> is a simplified version of <a href="http://www.gnu.org/software/make/">Make</a>
(<a href="http://rake.rubyforge.org/">Rake</a>, <a href="http://github.com/280north/jake">Jake</a>)
for CoffeeScript. You define tasks with names and descriptions in a Cakefile,
and can call them from the command line, or invoke them from other tasks.</p>
<p>Running <code>cake</code> with no arguments will print out a list of all the tasks in the
current directory's Cakefile.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>External dependencies.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">fs: </span> <span class="nx">require</span> <span class="s1">&#39;fs&#39;</span>
<span class="nv">path: </span> <span class="nx">require</span> <span class="s1">&#39;path&#39;</span>
<span class="nv">helpers: </span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./helpers&#39;</span><span class="p">).</span><span class="nx">helpers</span>
<span class="nv">optparse: </span> <span class="nx">require</span> <span class="s1">&#39;./optparse&#39;</span>
<span class="nv">CoffeeScript: </span><span class="nx">require</span> <span class="s1">&#39;./coffee-script&#39;</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>Keep track of the list of defined tasks, the accepted options, and so on.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">tasks: </span><span class="p">{}</span>
<span class="nv">options: </span><span class="p">{}</span>
<span class="nv">switches: </span><span class="p">[]</span>
<span class="nv">oparse: </span><span class="kc">null</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>Mixin the top-level Cake functions for Cakefiles to use directly.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">helpers</span><span class="p">.</span><span class="nx">extend</span> <span class="nx">global</span><span class="p">,</span> <span class="p">{</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>Define a Cake task with a short name, an optional sentence description,
and the function to run as the action itself.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">task: </span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">description</span><span class="p">,</span> <span class="nx">action</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="p">[</span><span class="nx">action</span><span class="p">,</span> <span class="nx">description</span><span class="p">]</span><span class="o">:</span> <span class="p">[</span><span class="nx">description</span><span class="p">,</span> <span class="nx">action</span><span class="p">]</span> <span class="nx">unless</span> <span class="nx">action</span>
<span class="nx">tasks</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span><span class="o">:</span> <span class="p">{</span><span class="nv">name: </span><span class="nx">name</span><span class="p">,</span> <span class="nv">description: </span><span class="nx">description</span><span class="p">,</span> <span class="nv">action: </span><span class="nx">action</span><span class="p">}</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p>Define an option that the Cakefile accepts. The parsed options hash,
containing all of the command-line options passed, will be made available
as the first argument to the action.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">option: </span><span class="p">(</span><span class="nx">letter</span><span class="p">,</span> <span class="nx">flag</span><span class="p">,</span> <span class="nx">description</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">switches</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="nx">letter</span><span class="p">,</span> <span class="nx">flag</span><span class="p">,</span> <span class="nx">description</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>Invoke another task in the current Cakefile.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">invoke: </span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">no_such_task</span> <span class="nx">name</span> <span class="nx">unless</span> <span class="nx">tasks</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span>
<span class="nx">tasks</span><span class="p">[</span><span class="nx">name</span><span class="p">].</span><span class="nx">action</span> <span class="nx">options</span>
<span class="p">}</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>Run <code>cake</code>. Executes all of the tasks you pass, in order. Note that Node's
asynchrony may cause tasks to execute in a different order than you'd expect.
If no tasks are passed, print the help screen.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.run: </span><span class="o">-&gt;</span>
<span class="nx">path</span><span class="p">.</span><span class="nx">exists</span> <span class="s1">&#39;Cakefile&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">exists</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&quot;Cakefile not found in ${process.cwd()}&quot;</span><span class="p">)</span> <span class="nx">unless</span> <span class="nx">exists</span>
<span class="nv">args: </span><span class="nx">process</span><span class="p">.</span><span class="nx">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">...</span><span class="nx">process</span><span class="p">.</span><span class="nx">argv</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span>
<span class="nx">CoffeeScript</span><span class="p">.</span><span class="nx">run</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="s1">&#39;Cakefile&#39;</span><span class="p">).</span><span class="nx">toString</span><span class="p">(),</span> <span class="p">{</span><span class="nv">source: </span><span class="s1">&#39;Cakefile&#39;</span><span class="p">}</span>
<span class="nv">oparse: </span><span class="k">new</span> <span class="nx">optparse</span><span class="p">.</span><span class="nx">OptionParser</span> <span class="nx">switches</span>
<span class="k">return</span> <span class="nx">print_tasks</span><span class="p">()</span> <span class="nx">unless</span> <span class="nx">args</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">options: </span><span class="nx">oparse</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span>
<span class="nx">invoke</span> <span class="nx">arg</span> <span class="k">for</span> <span class="nx">arg</span> <span class="k">in</span> <span class="nx">options</span><span class="p">.</span><span class="nx">arguments</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>Display the list of Cake tasks in a format similar to <code>rake -T</code></p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">print_tasks: </span><span class="o">-&gt;</span>
<span class="nx">puts</span> <span class="s1">&#39;&#39;</span>
<span class="k">for</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">task</span> <span class="k">of</span> <span class="nx">tasks</span>
<span class="nv">spaces: </span><span class="mi">20</span> <span class="o">-</span> <span class="nx">name</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">spaces: </span><span class="k">if</span> <span class="nx">spaces</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">then</span> <span class="p">(</span><span class="s1">&#39; &#39;</span> <span class="k">for</span> <span class="nx">i</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">spaces</span><span class="p">]).</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="k">else</span> <span class="s1">&#39;&#39;</span>
<span class="nv">desc: </span> <span class="k">if</span> <span class="nx">task</span><span class="p">.</span><span class="nx">description</span> <span class="k">then</span> <span class="s2">&quot;# $task.description&quot;</span> <span class="k">else</span> <span class="s1">&#39;&#39;</span>
<span class="nx">puts</span> <span class="s2">&quot;cake $name$spaces $desc&quot;</span>
<span class="nx">puts</span> <span class="nx">oparse</span><span class="p">.</span><span class="nx">help</span><span class="p">()</span> <span class="k">if</span> <span class="nx">switches</span><span class="p">.</span><span class="nx">length</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>Print an error and exit when attempting to all an undefined task.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">no_such_task: </span><span class="p">(</span><span class="nx">task</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">puts</span> <span class="s2">&quot;No such task: \&quot;$task\&quot;&quot;</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">exit</span> <span class="mi">1</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,65 @@
<!DOCTYPE html> <html> <head> <title>coffee-script.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> coffee-script.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>CoffeeScript can be used both on the server, as a command-line compiler based
on Node.js/V8, or to run CoffeeScripts directly in the browser. This module
contains the main entry functions for tokenzing, parsing, and compiling source
CoffeeScript into JavaScript.</p>
<p>If included on a webpage, it will automatically sniff out, compile, and
execute all scripts present in <code>text/coffeescript</code> tags.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>Set up dependencies correctly for both the server and the browser.</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">if</span> <span class="nx">process</span><span class="o">?</span>
<span class="nv">path: </span> <span class="nx">require</span> <span class="s1">&#39;path&#39;</span>
<span class="nv">Lexer: </span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./lexer&#39;</span><span class="p">).</span><span class="nx">Lexer</span>
<span class="nv">parser: </span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./parser&#39;</span><span class="p">).</span><span class="nx">parser</span>
<span class="nv">helpers: </span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./helpers&#39;</span><span class="p">).</span><span class="nx">helpers</span>
<span class="nx">helpers</span><span class="p">.</span><span class="nx">extend</span> <span class="nx">global</span><span class="p">,</span> <span class="nx">require</span> <span class="s1">&#39;./nodes&#39;</span>
<span class="k">if</span> <span class="nx">require</span><span class="p">.</span><span class="nx">registerExtension</span>
<span class="nx">require</span><span class="p">.</span><span class="nx">registerExtension</span> <span class="s1">&#39;.coffee&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">content</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nx">compile</span> <span class="nx">content</span>
<span class="k">else</span>
<span class="k">this</span><span class="p">.</span><span class="nv">exports: </span><span class="k">this</span><span class="p">.</span><span class="nv">CoffeeScript: </span><span class="p">{}</span>
<span class="nv">Lexer: </span> <span class="k">this</span><span class="p">.</span><span class="nx">Lexer</span>
<span class="nv">parser: </span> <span class="k">this</span><span class="p">.</span><span class="nx">parser</span>
<span class="nv">helpers: </span> <span class="k">this</span><span class="p">.</span><span class="nx">helpers</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>The current CoffeeScript version number.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.VERSION: </span><span class="s1">&#39;0.6.2&#39;</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>Instantiate a Lexer for our use here.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">lexer: </span><span class="k">new</span> <span class="nx">Lexer</span><span class="p">()</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison
compiler.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.compile: compile: </span><span class="p">(</span><span class="nx">code</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">options: </span><span class="o">or</span> <span class="p">{}</span>
<span class="k">try</span>
<span class="p">(</span><span class="nx">parser</span><span class="p">.</span><span class="nx">parse</span> <span class="nx">lexer</span><span class="p">.</span><span class="nx">tokenize</span> <span class="nx">code</span><span class="p">).</span><span class="nx">compile</span> <span class="nx">options</span>
<span class="k">catch</span> <span class="nx">err</span>
<span class="nv">err.message: </span><span class="s2">&quot;In $options.source, $err.message&quot;</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">source</span>
<span class="k">throw</span> <span class="nx">err</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p>Tokenize a string of CoffeeScript code, and return the array of tokens.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.tokens: </span><span class="p">(</span><span class="nx">code</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">lexer</span><span class="p">.</span><span class="nx">tokenize</span> <span class="nx">code</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>Tokenize and parse a string of CoffeeScript code, and return the AST. You can
then compile it by calling <code>.compile()</code> on the root, or traverse it by using
<code>.traverse()</code> with a callback.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.nodes: </span><span class="p">(</span><span class="nx">code</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">parser</span><span class="p">.</span><span class="nx">parse</span> <span class="nx">lexer</span><span class="p">.</span><span class="nx">tokenize</span> <span class="nx">code</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>Compile and execute a string of CoffeeScript (on the server), correctly
setting <code>__filename</code>, <code>__dirname</code>, and relative <code>require()</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.run: </span><span class="p">((</span><span class="nx">code</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">module.filename: __filename: </span><span class="nx">options</span><span class="p">.</span><span class="nx">source</span>
<span class="nv">__dirname: </span><span class="nx">path</span><span class="p">.</span><span class="nx">dirname</span><span class="p">(</span><span class="nx">__filename</span><span class="p">)</span>
<span class="nb">eval</span> <span class="nx">exports</span><span class="p">.</span><span class="nx">compile</span> <span class="nx">code</span><span class="p">,</span> <span class="nx">options</span>
<span class="p">)</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>Extend CoffeeScript with a custom language extension. It should hook in to
the <strong>Lexer</strong> (as a peer of any of the lexer's tokenizing methods), and
push a token on to the stack that contains a <strong>Node</strong> as the value (as a
peer of the nodes in <a href="nodes.html">nodes.coffee</a>).</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.extend: </span><span class="p">(</span><span class="nx">func</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">Lexer</span><span class="p">.</span><span class="nx">extensions</span><span class="p">.</span><span class="nx">push</span> <span class="nx">func</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>The real Lexer produces a generic stream of tokens. This object provides a
thin wrapper around it, compatible with the Jison API. We can then pass it
directly as a "Jison lexer".</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">parser.lexer: </span><span class="p">{</span>
<span class="nv">lex: </span><span class="o">-&gt;</span>
<span class="nv">token: </span><span class="nx">@tokens</span><span class="p">[</span><span class="nx">@pos</span><span class="p">]</span> <span class="o">or</span> <span class="p">[</span><span class="s2">&quot;&quot;</span><span class="p">]</span>
<span class="vi">@pos: </span><span class="o">+</span> <span class="mi">1</span>
<span class="k">this</span><span class="p">.</span><span class="nv">yylineno: </span><span class="nx">token</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nv">yytext: </span> <span class="nx">token</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nv">setInput: </span><span class="p">(</span><span class="nx">tokens</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="vi">@tokens: </span><span class="nx">tokens</span>
<span class="vi">@pos: </span><span class="mi">0</span>
<span class="nv">upcomingInput: </span><span class="o">-&gt;</span> <span class="s2">&quot;&quot;</span>
<span class="nv">showPosition: </span><span class="o">-&gt;</span> <span class="nx">@pos</span>
<span class="p">}</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-11">#</a> </div> <p>Activate CoffeeScript in the browser by having it compile and evaluate
all script tags with a content-type of <code>text/coffeescript</code>. This happens
on page load. Unfortunately, the text contents of remote scripts cannot be
accessed from the browser, so only inline script tags will work.</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">if</span> <span class="nb">document</span><span class="o">?</span> <span class="o">and</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span>
<span class="nv">process_scripts: </span><span class="o">-&gt;</span>
<span class="k">for</span> <span class="nx">tag</span> <span class="k">in</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="s1">&#39;script&#39;</span><span class="p">)</span> <span class="k">when</span> <span class="nx">tag</span><span class="p">.</span><span class="nx">type</span> <span class="o">is</span> <span class="s1">&#39;text/coffeescript&#39;</span>
<span class="nb">eval</span> <span class="nx">exports</span><span class="p">.</span><span class="nx">compile</span> <span class="nx">tag</span><span class="p">.</span><span class="nx">innerHTML</span>
<span class="k">if</span> <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span> <span class="s1">&#39;load&#39;</span><span class="p">,</span> <span class="nx">process_scripts</span><span class="p">,</span> <span class="kc">false</span>
<span class="k">else</span> <span class="k">if</span> <span class="nb">window</span><span class="p">.</span><span class="nx">attachEvent</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">attachEvent</span> <span class="s1">&#39;onload&#39;</span><span class="p">,</span> <span class="nx">process_scripts</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,126 @@
<!DOCTYPE html> <html> <head> <title>command.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> command.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>The <code>coffee</code> utility. Handles command-line compilation of CoffeeScript
into various forms: saved into <code>.js</code> files or printed to stdout, piped to
<a href="http://javascriptlint.com/">JSLint</a> or recompiled every time the source is
saved, printed as a token stream or as the syntax tree, or launch an
interactive REPL.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>External dependencies.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">fs: </span> <span class="nx">require</span> <span class="s1">&#39;fs&#39;</span>
<span class="nv">path: </span> <span class="nx">require</span> <span class="s1">&#39;path&#39;</span>
<span class="nv">optparse: </span> <span class="nx">require</span> <span class="s1">&#39;./optparse&#39;</span>
<span class="nv">CoffeeScript: </span> <span class="nx">require</span> <span class="s1">&#39;./coffee-script&#39;</span>
<span class="p">{</span><span class="nx">spawn</span><span class="p">,</span> <span class="nx">exec</span><span class="p">}</span><span class="o">:</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;child_process&#39;</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>The help banner that is printed when <code>coffee</code> is called without arguments.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">BANNER: </span><span class="s1">&#39;&#39;&#39;</span>
<span class="s1"> coffee compiles CoffeeScript source files into JavaScript.</span>
<span class="s1"> Usage:</span>
<span class="s1"> coffee path/to/script.coffee</span>
<span class="s1"> &#39;&#39;&#39;</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>The list of all the valid option flags that <code>coffee</code> knows how to handle.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">SWITCHES: </span><span class="p">[</span>
<span class="p">[</span><span class="s1">&#39;-c&#39;</span><span class="p">,</span> <span class="s1">&#39;--compile&#39;</span><span class="p">,</span> <span class="s1">&#39;compile to JavaScript and save as .js files&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-i&#39;</span><span class="p">,</span> <span class="s1">&#39;--interactive&#39;</span><span class="p">,</span> <span class="s1">&#39;run an interactive CoffeeScript REPL&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-o&#39;</span><span class="p">,</span> <span class="s1">&#39;--output [DIR]&#39;</span><span class="p">,</span> <span class="s1">&#39;set the directory for compiled JavaScript&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-w&#39;</span><span class="p">,</span> <span class="s1">&#39;--watch&#39;</span><span class="p">,</span> <span class="s1">&#39;watch scripts for changes, and recompile&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-p&#39;</span><span class="p">,</span> <span class="s1">&#39;--print&#39;</span><span class="p">,</span> <span class="s1">&#39;print the compiled JavaScript to stdout&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-l&#39;</span><span class="p">,</span> <span class="s1">&#39;--lint&#39;</span><span class="p">,</span> <span class="s1">&#39;pipe the compiled JavaScript through JSLint&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-s&#39;</span><span class="p">,</span> <span class="s1">&#39;--stdio&#39;</span><span class="p">,</span> <span class="s1">&#39;listen for and compile scripts over stdio&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-e&#39;</span><span class="p">,</span> <span class="s1">&#39;--eval&#39;</span><span class="p">,</span> <span class="s1">&#39;compile a string from the command line&#39;</span><span class="p">]</span>
<span class="p">[</span> <span class="s1">&#39;--no-wrap&#39;</span><span class="p">,</span> <span class="s1">&#39;compile without the top-level function wrapper&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-t&#39;</span><span class="p">,</span> <span class="s1">&#39;--tokens&#39;</span><span class="p">,</span> <span class="s1">&#39;print the tokens that the lexer produces&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-n&#39;</span><span class="p">,</span> <span class="s1">&#39;--nodes&#39;</span><span class="p">,</span> <span class="s1">&#39;print the parse tree that Jison produces&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-v&#39;</span><span class="p">,</span> <span class="s1">&#39;--version&#39;</span><span class="p">,</span> <span class="s1">&#39;display CoffeeScript version&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s1">&#39;-h&#39;</span><span class="p">,</span> <span class="s1">&#39;--help&#39;</span><span class="p">,</span> <span class="s1">&#39;display this help message&#39;</span><span class="p">]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>Top-level objects shared by all the functions.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">options: </span><span class="p">{}</span>
<span class="nv">sources: </span><span class="p">[]</span>
<span class="nv">option_parser: </span><span class="kc">null</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p>Run <code>coffee</code> by parsing passed options and determining what action to take.
Many flags cause us to divert before compiling anything. Flags passed after
<code>--</code> will be passed verbatim to your script as arguments in <code>process.argv</code></p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.run: </span><span class="o">-&gt;</span>
<span class="nx">parse_options</span><span class="p">()</span>
<span class="k">return</span> <span class="nx">usage</span><span class="p">()</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">help</span>
<span class="k">return</span> <span class="nx">version</span><span class="p">()</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">version</span>
<span class="k">return</span> <span class="nx">require</span> <span class="s1">&#39;./repl&#39;</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">interactive</span>
<span class="k">return</span> <span class="nx">compile_stdio</span><span class="p">()</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">stdio</span>
<span class="k">return</span> <span class="nx">compile_script</span> <span class="s1">&#39;console&#39;</span><span class="p">,</span> <span class="nx">sources</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nb">eval</span>
<span class="k">return</span> <span class="nx">usage</span><span class="p">()</span> <span class="nx">unless</span> <span class="nx">sources</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">separator: </span><span class="nx">sources</span><span class="p">.</span><span class="nx">indexOf</span> <span class="s1">&#39;--&#39;</span>
<span class="nv">flags: </span><span class="p">[]</span>
<span class="k">if</span> <span class="nx">separator</span> <span class="o">&gt;=</span> <span class="mi">0</span>
<span class="nv">flags: </span><span class="nx">sources</span><span class="p">[(</span><span class="nx">separator</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)...</span><span class="nx">sources</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span>
<span class="nv">sources: </span><span class="nx">sources</span><span class="p">[</span><span class="mi">0</span><span class="p">...</span><span class="nx">separator</span><span class="p">]</span>
<span class="nv">process.ARGV: process.argv: </span><span class="nx">flags</span>
<span class="nx">compile_scripts</span><span class="p">()</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>Asynchronously read in each CoffeeScript in a list of source files and
compile them. If a directory is passed, recursively compile all source
files in it and all subdirectories.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">compile_scripts: </span><span class="o">-&gt;</span>
<span class="k">for</span> <span class="nx">source</span> <span class="k">in</span> <span class="nx">sources</span>
<span class="nv">base: </span><span class="nx">source</span>
<span class="nv">compile: </span><span class="p">(</span><span class="nx">source</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">path</span><span class="p">.</span><span class="nx">exists</span> <span class="nx">source</span><span class="p">,</span> <span class="p">(</span><span class="nx">exists</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span> <span class="s2">&quot;File not found: $source&quot;</span> <span class="nx">unless</span> <span class="nx">exists</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">stat</span> <span class="nx">source</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">stats</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">if</span> <span class="nx">stats</span><span class="p">.</span><span class="nx">isDirectory</span><span class="p">()</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">readdir</span> <span class="nx">source</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">files</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">for</span> <span class="nx">file</span> <span class="k">in</span> <span class="nx">files</span>
<span class="nx">compile</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">source</span><span class="p">,</span> <span class="nx">file</span><span class="p">)</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">path</span><span class="p">.</span><span class="nx">extname</span><span class="p">(</span><span class="nx">source</span><span class="p">)</span> <span class="o">is</span> <span class="s1">&#39;.coffee&#39;</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span> <span class="nx">source</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">code</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nx">compile_script</span><span class="p">(</span><span class="nx">source</span><span class="p">,</span> <span class="nx">code</span><span class="p">.</span><span class="nx">toString</span><span class="p">(),</span> <span class="nx">base</span><span class="p">)</span>
<span class="nx">watch</span> <span class="nx">source</span><span class="p">,</span> <span class="nx">base</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">watch</span>
<span class="nx">compile</span> <span class="nx">source</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>Compile a single source script, containing the given code, according to the
requested options. If evaluating the script directly sets <code>__filename</code>,
<code>__dirname</code> and <code>module.filename</code> to be correct relative to the script's path.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">compile_script: </span><span class="p">(</span><span class="nx">source</span><span class="p">,</span> <span class="nx">code</span><span class="p">,</span> <span class="nx">base</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">o: </span><span class="nx">options</span>
<span class="nv">code_opts: </span><span class="nx">compile_options</span> <span class="nx">source</span>
<span class="k">try</span>
<span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">tokens</span> <span class="k">then</span> <span class="nx">print_tokens</span> <span class="nx">CoffeeScript</span><span class="p">.</span><span class="nx">tokens</span> <span class="nx">code</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">nodes</span> <span class="k">then</span> <span class="nx">puts</span> <span class="nx">CoffeeScript</span><span class="p">.</span><span class="nx">nodes</span><span class="p">(</span><span class="nx">code</span><span class="p">).</span><span class="nx">toString</span><span class="p">()</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">run</span> <span class="k">then</span> <span class="nx">CoffeeScript</span><span class="p">.</span><span class="nx">run</span> <span class="nx">code</span><span class="p">,</span> <span class="nx">code_opts</span>
<span class="k">else</span>
<span class="nv">js: </span><span class="nx">CoffeeScript</span><span class="p">.</span><span class="nx">compile</span> <span class="nx">code</span><span class="p">,</span> <span class="nx">code_opts</span>
<span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">print</span> <span class="k">then</span> <span class="nx">print</span> <span class="nx">js</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">compile</span> <span class="k">then</span> <span class="nx">write_js</span> <span class="nx">source</span><span class="p">,</span> <span class="nx">js</span><span class="p">,</span> <span class="nx">base</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">lint</span> <span class="k">then</span> <span class="nx">lint</span> <span class="nx">js</span>
<span class="k">catch</span> <span class="nx">err</span>
<span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">watch</span> <span class="k">then</span> <span class="nx">puts</span> <span class="nx">err</span><span class="p">.</span><span class="nx">message</span> <span class="k">else</span> <span class="k">throw</span> <span class="nx">err</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>Attach the appropriate listeners to compile scripts incoming over <strong>stdin</strong>,
and write them back to <strong>stdout</strong>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">compile_stdio: </span><span class="o">-&gt;</span>
<span class="nv">code: </span><span class="s1">&#39;&#39;</span>
<span class="nv">stdin: </span><span class="nx">process</span><span class="p">.</span><span class="nx">openStdin</span><span class="p">()</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">addListener</span> <span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">buffer</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">code: </span><span class="o">+</span> <span class="nx">buffer</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span> <span class="k">if</span> <span class="nx">buffer</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">addListener</span> <span class="s1">&#39;end&#39;</span><span class="p">,</span> <span class="o">-&gt;</span>
<span class="nx">compile_script</span> <span class="s1">&#39;stdio&#39;</span><span class="p">,</span> <span class="nx">code</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>Watch a source CoffeeScript file using <code>fs.watchFile</code>, recompiling it every
time the file is updated. May be used in combination with other options,
such as <code>--lint</code> or <code>--print</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">watch: </span><span class="p">(</span><span class="nx">source</span><span class="p">,</span> <span class="nx">base</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">watchFile</span> <span class="nx">source</span><span class="p">,</span> <span class="p">{</span><span class="nv">persistent: </span><span class="kc">true</span><span class="p">,</span> <span class="nv">interval: </span><span class="mi">500</span><span class="p">},</span> <span class="p">(</span><span class="nx">curr</span><span class="p">,</span> <span class="nx">prev</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">curr</span><span class="p">.</span><span class="nx">mtime</span><span class="p">.</span><span class="nx">getTime</span><span class="p">()</span> <span class="o">is</span> <span class="nx">prev</span><span class="p">.</span><span class="nx">mtime</span><span class="p">.</span><span class="nx">getTime</span><span class="p">()</span>
<span class="nx">puts</span> <span class="s2">&quot;Compiled $source&quot;</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">compile</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span> <span class="nx">source</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">code</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nx">compile_script</span><span class="p">(</span><span class="nx">source</span><span class="p">,</span> <span class="nx">code</span><span class="p">.</span><span class="nx">toString</span><span class="p">(),</span> <span class="nx">base</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-11">#</a> </div> <p>Write out a JavaScript source file with the compiled code. By default, files
are written out in <code>cwd</code> as <code>.js</code> files with the same name, but the output
directory can be customized with <code>--output</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">write_js: </span><span class="p">(</span><span class="nx">source</span><span class="p">,</span> <span class="nx">js</span><span class="p">,</span> <span class="nx">base</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">filename: </span><span class="nx">path</span><span class="p">.</span><span class="nx">basename</span><span class="p">(</span><span class="nx">source</span><span class="p">,</span> <span class="nx">path</span><span class="p">.</span><span class="nx">extname</span><span class="p">(</span><span class="nx">source</span><span class="p">))</span> <span class="o">+</span> <span class="s1">&#39;.js&#39;</span>
<span class="nv">src_dir: </span> <span class="nx">path</span><span class="p">.</span><span class="nx">dirname</span> <span class="nx">source</span>
<span class="nv">base_dir: </span><span class="nx">src_dir</span><span class="p">.</span><span class="nx">substring</span> <span class="nx">base</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">dir: </span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">output</span> <span class="k">then</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span> <span class="nx">options</span><span class="p">.</span><span class="nx">output</span><span class="p">,</span> <span class="nx">base_dir</span> <span class="k">else</span> <span class="nx">src_dir</span>
<span class="nv">js_path: </span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span> <span class="nx">dir</span><span class="p">,</span> <span class="nx">filename</span>
<span class="nv">compile: </span> <span class="o">-&gt;</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span> <span class="nx">js_path</span><span class="p">,</span> <span class="nx">js</span>
<span class="nx">path</span><span class="p">.</span><span class="nx">exists</span> <span class="nx">dir</span><span class="p">,</span> <span class="p">(</span><span class="nx">exists</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">if</span> <span class="nx">exists</span> <span class="k">then</span> <span class="nx">compile</span><span class="p">()</span> <span class="k">else</span> <span class="nx">exec</span> <span class="s2">&quot;mkdir -p $dir&quot;</span><span class="p">,</span> <span class="nx">compile</span></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-12">#</a> </div> <p>Pipe compiled JS through JSLint (requires a working <code>jsl</code> command), printing
any errors or warnings that arise.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">lint: </span><span class="p">(</span><span class="nx">js</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">print_it: </span><span class="p">(</span><span class="nx">buffer</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nx">print</span> <span class="nx">buffer</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span>
<span class="nv">jsl: </span><span class="nx">spawn</span> <span class="s1">&#39;jsl&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;-nologo&#39;</span><span class="p">,</span> <span class="s1">&#39;-stdin&#39;</span><span class="p">]</span>
<span class="nx">jsl</span><span class="p">.</span><span class="nx">stdout</span><span class="p">.</span><span class="nx">addListener</span> <span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="nx">print_it</span>
<span class="nx">jsl</span><span class="p">.</span><span class="nx">stderr</span><span class="p">.</span><span class="nx">addListener</span> <span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="nx">print_it</span>
<span class="nx">jsl</span><span class="p">.</span><span class="nx">stdin</span><span class="p">.</span><span class="nx">write</span> <span class="nx">js</span>
<span class="nx">jsl</span><span class="p">.</span><span class="nx">stdin</span><span class="p">.</span><span class="nx">end</span><span class="p">()</span></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-13">#</a> </div> <p>Pretty-print a stream of tokens.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">print_tokens: </span><span class="p">(</span><span class="nx">tokens</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">strings: </span><span class="k">for</span> <span class="nx">token</span> <span class="k">in</span> <span class="nx">tokens</span>
<span class="p">[</span><span class="nx">tag</span><span class="p">,</span> <span class="nx">value</span><span class="p">]</span><span class="o">:</span> <span class="p">[</span><span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">token</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">toString</span><span class="p">().</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\n/</span><span class="p">,</span> <span class="s1">&#39;\\n&#39;</span><span class="p">)]</span>
<span class="s2">&quot;[$tag $value]&quot;</span>
<span class="nx">puts</span> <span class="nx">strings</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-14">#</a> </div> <p>Use the <a href="optparse.html">OptionParser module</a> to extract all options from
<code>process.argv</code> that are specified in <code>SWITCHES</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">parse_options: </span><span class="o">-&gt;</span>
<span class="nv">option_parser: </span><span class="k">new</span> <span class="nx">optparse</span><span class="p">.</span><span class="nx">OptionParser</span> <span class="nx">SWITCHES</span><span class="p">,</span> <span class="nx">BANNER</span>
<span class="nv">o: options: </span> <span class="nx">option_parser</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">argv</span><span class="p">)</span>
<span class="nv">options.run: </span> <span class="o">not</span> <span class="p">(</span><span class="nx">o</span><span class="p">.</span><span class="nx">compile</span> <span class="o">or</span> <span class="nx">o</span><span class="p">.</span><span class="nx">print</span> <span class="o">or</span> <span class="nx">o</span><span class="p">.</span><span class="nx">lint</span><span class="p">)</span>
<span class="nv">options.print: </span><span class="o">!!</span> <span class="p">(</span><span class="nx">o</span><span class="p">.</span><span class="nx">print</span> <span class="o">or</span> <span class="p">(</span><span class="nx">o</span><span class="p">.</span><span class="nb">eval</span> <span class="o">or</span> <span class="nx">o</span><span class="p">.</span><span class="nx">stdio</span> <span class="o">and</span> <span class="nx">o</span><span class="p">.</span><span class="nx">compile</span><span class="p">))</span>
<span class="nv">sources: </span> <span class="nx">options</span><span class="p">.</span><span class="nx">arguments</span><span class="p">[</span><span class="mi">2</span><span class="p">...</span><span class="nx">options</span><span class="p">.</span><span class="nx">arguments</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-15"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-15">#</a> </div> <p>The compile-time options to pass to the CoffeeScript compiler.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">compile_options: </span><span class="p">(</span><span class="nx">source</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">o: </span><span class="p">{</span><span class="nv">source: </span><span class="nx">source</span><span class="p">}</span>
<span class="nx">o</span><span class="p">[</span><span class="s1">&#39;no_wrap&#39;</span><span class="p">]</span><span class="o">:</span> <span class="nx">options</span><span class="p">[</span><span class="s1">&#39;no-wrap&#39;</span><span class="p">]</span>
<span class="nx">o</span></pre></div> </td> </tr> <tr id="section-16"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-16">#</a> </div> <p>Print the <code>--help</code> usage message and exit.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">usage: </span><span class="o">-&gt;</span>
<span class="nx">puts</span> <span class="nx">option_parser</span><span class="p">.</span><span class="nx">help</span><span class="p">()</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">exit</span> <span class="mi">0</span></pre></div> </td> </tr> <tr id="section-17"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-17">#</a> </div> <p>Print the <code>--version</code> message and exit.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">version: </span><span class="o">-&gt;</span>
<span class="nx">puts</span> <span class="s2">&quot;CoffeeScript version $CoffeeScript.VERSION&quot;</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">exit</span> <span class="mi">0</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,185 @@
/*--------------------- Layout and Typography ----------------------------*/
body {
font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
font-size: 16px;
line-height: 24px;
color: #252519;
margin: 0; padding: 0;
}
a {
color: #261a3b;
}
a:visited {
color: #261a3b;
}
p {
margin: 0 0 15px 0;
}
h1, h2, h3, h4, h5, h6 {
margin: 40px 0 15px 0;
}
h3, h4, h5, h6 {
margin-top: 20px;
}
#container {
position: relative;
}
#background {
position: fixed;
top: 0; left: 580px; right: 0; bottom: 0;
background: #f5f5ff;
border-left: 1px solid #e5e5ee;
z-index: -1;
}
#jump_to, #jump_page {
background: white;
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
font: 10px Arial;
text-transform: uppercase;
cursor: pointer;
text-align: right;
}
#jump_to, #jump_wrapper {
position: fixed;
right: 0; top: 0;
padding: 5px 10px;
}
#jump_wrapper {
padding: 0;
display: none;
}
#jump_to:hover #jump_wrapper {
display: block;
}
#jump_page {
padding: 5px 0 3px;
margin: 0 0 25px 25px;
}
#jump_page .source {
display: block;
padding: 5px 10px;
text-decoration: none;
border-top: 1px solid #eee;
}
#jump_page .source:hover {
background: #f5f5ff;
}
#jump_page .source:first-child {
}
table td {
border: 0;
outline: 0;
}
td.docs, th.docs {
max-width: 500px;
min-width: 500px;
min-height: 5px;
padding: 10px 25px 1px 50px;
vertical-align: top;
text-align: left;
}
.docs pre {
margin: 15px 0 15px;
padding-left: 15px;
}
.docs p tt, .docs p code {
background: #f8f8ff;
border: 1px solid #dedede;
font-size: 12px;
padding: 0 0.2em;
}
.octowrap {
position: relative;
}
.octothorpe {
font: 12px Arial;
text-decoration: none;
color: #454545;
position: absolute;
top: 3px; left: -20px;
padding: 1px 2px;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
}
td.docs:hover .octothorpe {
opacity: 1;
}
td.code, th.code {
padding: 14px 15px 16px 50px;
width: 100%;
vertical-align: top;
background: #f5f5ff;
border-left: 1px solid #e5e5ee;
}
pre, tt, code {
font-size: 12px; line-height: 18px;
font-family: Monaco, Consolas, "Lucida Console", monospace;
margin: 0; padding: 0;
}
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
body .hll { background-color: #ffffcc }
body .c { color: #408080; font-style: italic } /* Comment */
body .err { border: 1px solid #FF0000 } /* Error */
body .k { color: #954121 } /* Keyword */
body .o { color: #666666 } /* Operator */
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
body .cp { color: #BC7A00 } /* Comment.Preproc */
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
body .cs { color: #408080; font-style: italic } /* Comment.Special */
body .gd { color: #A00000 } /* Generic.Deleted */
body .ge { font-style: italic } /* Generic.Emph */
body .gr { color: #FF0000 } /* Generic.Error */
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
body .gi { color: #00A000 } /* Generic.Inserted */
body .go { color: #808080 } /* Generic.Output */
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
body .gs { font-weight: bold } /* Generic.Strong */
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
body .gt { color: #0040D0 } /* Generic.Traceback */
body .kc { color: #954121 } /* Keyword.Constant */
body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
body .kp { color: #954121 } /* Keyword.Pseudo */
body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
body .kt { color: #B00040 } /* Keyword.Type */
body .m { color: #666666 } /* Literal.Number */
body .s { color: #219161 } /* Literal.String */
body .na { color: #7D9029 } /* Name.Attribute */
body .nb { color: #954121 } /* Name.Builtin */
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
body .no { color: #880000 } /* Name.Constant */
body .nd { color: #AA22FF } /* Name.Decorator */
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
body .nf { color: #0000FF } /* Name.Function */
body .nl { color: #A0A000 } /* Name.Label */
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
body .nt { color: #954121; font-weight: bold } /* Name.Tag */
body .nv { color: #19469D } /* Name.Variable */
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
body .w { color: #bbbbbb } /* Text.Whitespace */
body .mf { color: #666666 } /* Literal.Number.Float */
body .mh { color: #666666 } /* Literal.Number.Hex */
body .mi { color: #666666 } /* Literal.Number.Integer */
body .mo { color: #666666 } /* Literal.Number.Oct */
body .sb { color: #219161 } /* Literal.String.Backtick */
body .sc { color: #219161 } /* Literal.String.Char */
body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
body .s2 { color: #219161 } /* Literal.String.Double */
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
body .sh { color: #219161 } /* Literal.String.Heredoc */
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
body .sx { color: #954121 } /* Literal.String.Other */
body .sr { color: #BB6688 } /* Literal.String.Regex */
body .s1 { color: #219161 } /* Literal.String.Single */
body .ss { color: #19469D } /* Literal.String.Symbol */
body .bp { color: #954121 } /* Name.Builtin.Pseudo */
body .vc { color: #19469D } /* Name.Variable.Class */
body .vg { color: #19469D } /* Name.Variable.Global */
body .vi { color: #19469D } /* Name.Variable.Instance */
body .il { color: #666666 } /* Literal.Number.Integer.Long */

View File

@@ -0,0 +1,423 @@
<!DOCTYPE html> <html> <head> <title>grammar.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> grammar.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>The CoffeeScript parser is generated by <a href="http://github.com/zaach/jison">Jison</a>
from this grammar file. Jison is a bottom-up parser generator, similar in
style to <a href="http://www.gnu.org/software/bison">Bison</a>, implemented in JavaScript.
It can recognize <a href="http://en.wikipedia.org/wiki/LR_grammar">LALR(1), LR(0), SLR(1), and LR(1)</a>
type grammars. To create the Jison parser, we list the pattern to match
on the left-hand side, and the action to take (usually the creation of syntax
tree nodes) on the right. As the parser runs, it
shifts tokens from our token stream, from left to right, and
<a href="http://en.wikipedia.org/wiki/Bottom-up_parsing">attempts to match</a>
the token sequence against the rules below. When a match can be made, it
reduces into the <a href="http://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols">nonterminal</a>
(the enclosing name at the top), and we proceed from there.</p>
<p>If you run the <code>cake build:parser</code> command, Jison constructs a parse table
from our rules and saves it into <code>lib/parser.js</code>.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>The only dependency is on the <strong>Jison.Parser</strong>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">Parser: </span><span class="nx">require</span><span class="p">(</span><span class="s1">&#39;jison&#39;</span><span class="p">).</span><span class="nx">Parser</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <h2>Jison DSL</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>Since we're going to be wrapped in a function by Jison in any case, if our
action immediately returns a value, we can optimize by removing the function
wrapper and just returning the value directly.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">unwrap: </span><span class="sr">/function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>Our handy DSL for Jison grammar generation, thanks to
<a href="http://github.com/creationix">Tim Caswell</a>. For every rule in the grammar,
we pass the pattern-defining string, the action to run, and extra options,
optionally. If no action is specified, we simply pass the value of the
previous nonterminal.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">o: </span><span class="p">(</span><span class="nx">pattern_string</span><span class="p">,</span> <span class="nx">action</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">return</span> <span class="p">[</span><span class="nx">pattern_string</span><span class="p">,</span> <span class="s1">&#39;$$ = $1;&#39;</span><span class="p">,</span> <span class="nx">options</span><span class="p">]</span> <span class="nx">unless</span> <span class="nx">action</span>
<span class="nv">action: </span><span class="k">if</span> <span class="nv">match: </span><span class="p">(</span><span class="nx">action</span> <span class="o">+</span> <span class="s1">&#39;&#39;</span><span class="p">).</span><span class="nx">match</span><span class="p">(</span><span class="nx">unwrap</span><span class="p">)</span> <span class="k">then</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">else</span> <span class="s2">&quot;($action())&quot;</span>
<span class="p">[</span><span class="nx">pattern_string</span><span class="p">,</span> <span class="s2">&quot;$$ = $action;&quot;</span><span class="p">,</span> <span class="nx">options</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <h2>Grammatical Rules</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>In all of the rules that follow, you'll see the name of the nonterminal as
the key to a list of alternative matches. With each match's action, the
dollar-sign variables are provided by Jison as references to the value of
their numeric position, so in this rule:</p>
<pre><code>"Expression UNLESS Expression"
</code></pre>
<p><code>$1</code> would be the value of the first <code>Expression</code>, <code>$2</code> would be the token
for the <code>UNLESS</code> terminal, and <code>$3</code> would be the value of the second
<code>Expression</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">grammar: </span><span class="p">{</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>The <strong>Root</strong> is the top-level node in the syntax tree. Since we parse bottom-up,
all parsing must end here.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Root: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">Expressions</span><span class="p">()</span>
<span class="nx">o</span> <span class="s2">&quot;TERMINATOR&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">Expressions</span><span class="p">()</span>
<span class="nx">o</span> <span class="s2">&quot;Body&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Block TERMINATOR&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>Any list of statements and expressions, seperated by line breaks or semicolons.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Body: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Line&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">Expressions</span><span class="p">.</span><span class="nx">wrap</span> <span class="p">[</span><span class="nx">$1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;Body TERMINATOR Line&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">push</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Body TERMINATOR&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>Expressions and statements, which make up a line in a body.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Line: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Expression&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Statement&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-11">#</a> </div> <p>Pure statements which cannot be expressions.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Statement: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Return&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Throw&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;BREAK&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;CONTINUE&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-12">#</a> </div> <p>All the different types of expressions in our language. The basic unit of
CoffeeScript is the <strong>Expression</strong> -- everything that can be an expression
is one. Expressions serve as the building blocks of many other rules, making
them somewhat circular.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Expression: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Value&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Call&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Curry&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Code&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Operation&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Assign&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;If&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Try&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;While&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;For&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Switch&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Extends&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Class&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Splat&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Existence&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Comment&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Extension&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-13">#</a> </div> <p>A an indented block of expressions. Note that the <a href="rewriter.html">Rewriter</a>
will convert some postfix forms into blocks for us, by adjusting the
token stream.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Block: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;INDENT Body OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;INDENT OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">Expressions</span><span class="p">()</span>
<span class="nx">o</span> <span class="s2">&quot;TERMINATOR Comment&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">Expressions</span><span class="p">.</span><span class="nx">wrap</span> <span class="p">[</span><span class="nx">$2</span><span class="p">]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-14">#</a> </div> <p>A literal identifier, a variable name or property.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Identifier: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;IDENTIFIER&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-15"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-15">#</a> </div> <p>Alphanumerics are separated from the other <strong>Literal</strong> matchers because
they can also serve as keys in object literals.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">AlphaNumeric: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;NUMBER&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;STRING&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-16"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-16">#</a> </div> <p>All of our immediate values. These can (in general), be passed straight
through and printed to JavaScript.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Literal: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;AlphaNumeric&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;JS&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;REGEX&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;TRUE&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="kc">true</span>
<span class="nx">o</span> <span class="s2">&quot;FALSE&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="kc">false</span>
<span class="nx">o</span> <span class="s2">&quot;YES&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="kc">true</span>
<span class="nx">o</span> <span class="s2">&quot;NO&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="kc">false</span>
<span class="nx">o</span> <span class="s2">&quot;ON&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="kc">true</span>
<span class="nx">o</span> <span class="s2">&quot;OFF&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="kc">false</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-17"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-17">#</a> </div> <p>Assignment of a variable, property, or index to a value.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Assign: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Assignable ASSIGN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">AssignNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-18"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-18">#</a> </div> <p>Assignment when it happens within an object literal. The difference from
the ordinary <strong>Assign</strong> is that these allow numbers and strings as keys.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">AssignObj: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Identifier&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;AlphaNumeric&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Identifier ASSIGN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">AssignNode</span> <span class="k">new</span> <span class="nx">ValueNode</span><span class="p">(</span><span class="nx">$1</span><span class="p">),</span> <span class="nx">$3</span><span class="p">,</span> <span class="s1">&#39;object&#39;</span>
<span class="nx">o</span> <span class="s2">&quot;AlphaNumeric ASSIGN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">AssignNode</span> <span class="k">new</span> <span class="nx">ValueNode</span><span class="p">(</span><span class="nx">$1</span><span class="p">),</span> <span class="nx">$3</span><span class="p">,</span> <span class="s1">&#39;object&#39;</span>
<span class="nx">o</span> <span class="s2">&quot;Comment&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-19"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-19">#</a> </div> <p>A return statement from a function body.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Return: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;RETURN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ReturnNode</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;RETURN&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ReturnNode</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="s1">&#39;null&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-20"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-20">#</a> </div> <p>A comment. Because CoffeeScript passes comments through to JavaScript, we
have to parse comments like any other construct, and identify all of the
positions in which they can occur in the grammar.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Comment: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;COMMENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">CommentNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;HERECOMMENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">CommentNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="s1">&#39;herecomment&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-21"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-21">#</a> </div> <p><a href="http://jashkenas.github.com/coffee-script/#existence">The existential operator</a>.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Existence: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Expression ?&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ExistenceNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-22"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-22">#</a> </div> <p>The <strong>Code</strong> node is the function literal. It's defined by an indented block
of <strong>Expressions</strong> preceded by a function arrow, with an optional parameter
list.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Code: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;PARAM_START ParamList PARAM_END FuncGlyph Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">CodeNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$5</span><span class="p">,</span> <span class="nx">$4</span>
<span class="nx">o</span> <span class="s2">&quot;FuncGlyph Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">CodeNode</span> <span class="p">[],</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-23"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-23">#</a> </div> <p>CoffeeScript has two different symbols for functions. <code>-&gt;</code> is for ordinary
functions, and <code>=&gt;</code> is for functions bound to the current value of <em>this</em>.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">FuncGlyph: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;-&gt;&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="s1">&#39;func&#39;</span>
<span class="nx">o</span> <span class="s2">&quot;=&gt;&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="s1">&#39;boundfunc&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-24"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-24">#</a> </div> <p>An optional, trailing comma.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">OptComma: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s1">&#39;&#39;</span>
<span class="nx">o</span> <span class="s1">&#39;,&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-25"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-25">#</a> </div> <p>The list of parameters that a function accepts can be of any length.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ParamList: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[]</span>
<span class="nx">o</span> <span class="s2">&quot;Param&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nx">$1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ParamList , Param&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[</span><span class="nx">$3</span><span class="p">]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-26"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-26">#</a> </div> <p>A single parameter in a function definition can be ordinary, or a splat
that hoovers up the remaining arguments.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Param: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;PARAM&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;Param . . .&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">SplatNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-27"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-27">#</a> </div> <p>A splat that occurs outside of a parameter list.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Splat: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Expression . . .&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">SplatNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-28"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-28">#</a> </div> <p>Variables and properties that can be assigned to.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">SimpleAssignable: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Identifier&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;Value Accessor&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">push</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;Invocation Accessor&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="p">[</span><span class="nx">$2</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ThisProperty&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-29"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-29">#</a> </div> <p>Everything that can be assigned to.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Assignable: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;SimpleAssignable&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Array&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;Object&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-30"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-30">#</a> </div> <p>The types of things that can be treated as values -- assigned to, invoked
as functions, indexed into, named as a class, etc.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Value: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Assignable&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Literal&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;Parenthetical&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;Range&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;This&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;NULL&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="s1">&#39;null&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-31"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-31">#</a> </div> <p>The general group of accessors into an object, by property, by prototype
or by array index or slice.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Accessor: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;PROPERTY_ACCESS Identifier&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">AccessorNode</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;PROTOTYPE_ACCESS Identifier&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">AccessorNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="s1">&#39;prototype&#39;</span>
<span class="nx">o</span> <span class="s2">&quot;::&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">AccessorNode</span><span class="p">(</span><span class="k">new</span> <span class="nx">LiteralNode</span><span class="p">(</span><span class="s1">&#39;prototype&#39;</span><span class="p">))</span>
<span class="nx">o</span> <span class="s2">&quot;SOAK_ACCESS Identifier&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">AccessorNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="s1">&#39;soak&#39;</span>
<span class="nx">o</span> <span class="s2">&quot;Index&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Slice&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">SliceNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-32"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-32">#</a> </div> <p>Indexing into an object or array using bracket notation.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Index: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;INDEX_START Expression INDEX_END&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IndexNode</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;SOAKED_INDEX_START Expression SOAKED_INDEX_END&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IndexNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="s1">&#39;soak&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-33"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-33">#</a> </div> <p>In CoffeeScript, an object literal is simply a list of assignments.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nb">Object</span><span class="o">:</span> <span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;{ AssignList OptComma }&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ObjectNode</span> <span class="nx">$2</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-34"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-34">#</a> </div> <p>Assignment of properties within an object literal can be separated by
comma, as in JavaScript, or simply by newline.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">AssignList: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[]</span>
<span class="nx">o</span> <span class="s2">&quot;AssignObj&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nx">$1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;AssignList , AssignObj&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[</span><span class="nx">$3</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;AssignList OptComma TERMINATOR AssignObj&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[</span><span class="nx">$4</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;AssignList OptComma INDENT AssignList OptComma OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="nx">$4</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-35"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-35">#</a> </div> <p>Class definitions have optional bodies of prototype property assignments,
and optional references to the superclass.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Class: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;CLASS SimpleAssignable&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ClassNode</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;CLASS SimpleAssignable EXTENDS Value&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ClassNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$4</span>
<span class="nx">o</span> <span class="s2">&quot;CLASS SimpleAssignable INDENT ClassBody OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ClassNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="nx">$4</span>
<span class="nx">o</span> <span class="s2">&quot;CLASS SimpleAssignable EXTENDS Value INDENT ClassBody OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ClassNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$4</span><span class="p">,</span> <span class="nx">$6</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-36"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-36">#</a> </div> <p>Assignments that can happen directly inside a class declaration.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ClassAssign: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;AssignObj&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;ThisProperty ASSIGN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">AssignNode</span> <span class="k">new</span> <span class="nx">ValueNode</span><span class="p">(</span><span class="nx">$1</span><span class="p">),</span> <span class="nx">$3</span><span class="p">,</span> <span class="s1">&#39;this&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-37"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-37">#</a> </div> <p>A list of assignments to a class.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ClassBody: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[]</span>
<span class="nx">o</span> <span class="s2">&quot;ClassAssign&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nx">$1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ClassBody TERMINATOR ClassAssign&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="nx">$3</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-38"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-38">#</a> </div> <p>The three flavors of function call: normal, object instantiation with <code>new</code>,
and calling <code>super()</code></p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Call: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Invocation&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;NEW Invocation&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$2</span><span class="p">.</span><span class="nx">new_instance</span><span class="p">()</span>
<span class="nx">o</span> <span class="s2">&quot;Super&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-39"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-39">#</a> </div> <p>Binds a function call to a context and/or arguments.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Curry: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Value &lt;- Arguments&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">CurryNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-40"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-40">#</a> </div> <p>Extending an object by setting its prototype chain to reference a parent
object.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Extends: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;SimpleAssignable EXTENDS Value&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ExtendsNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-41"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-41">#</a> </div> <p>Ordinary function invocation, or a chained series of calls.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Invocation: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Value Arguments&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">CallNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;Invocation Arguments&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">CallNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$2</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-42"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-42">#</a> </div> <p>The list of arguments to a function call.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Arguments: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;CALL_START ArgList OptComma CALL_END&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$2</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-43"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-43">#</a> </div> <p>Calling super.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Super: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;SUPER CALL_START ArgList OptComma CALL_END&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">CallNode</span> <span class="s1">&#39;super&#39;</span><span class="p">,</span> <span class="nx">$3</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-44"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-44">#</a> </div> <p>A reference to the <em>this</em> current object.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">This: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;THIS&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="s1">&#39;this&#39;</span>
<span class="nx">o</span> <span class="s2">&quot;@&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="k">new</span> <span class="nx">LiteralNode</span> <span class="s1">&#39;this&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-45"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-45">#</a> </div> <p>A reference to a property on <em>this</em>.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ThisProperty: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;@ Identifier&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="k">new</span> <span class="nx">LiteralNode</span><span class="p">(</span><span class="s1">&#39;this&#39;</span><span class="p">),</span> <span class="p">[</span><span class="k">new</span> <span class="nx">AccessorNode</span><span class="p">(</span><span class="nx">$2</span><span class="p">)]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-46"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-46">#</a> </div> <p>The CoffeeScript range literal.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Range: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;[ Expression . . Expression ]&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">RangeNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$5</span>
<span class="nx">o</span> <span class="s2">&quot;[ Expression . . . Expression ]&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">RangeNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$6</span><span class="p">,</span> <span class="kc">true</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-47"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-47">#</a> </div> <p>The slice literal.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Slice: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;INDEX_START Expression . . Expression INDEX_END&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">RangeNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$5</span>
<span class="nx">o</span> <span class="s2">&quot;INDEX_START Expression . . . Expression INDEX_END&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">RangeNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$6</span><span class="p">,</span> <span class="kc">true</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-48"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-48">#</a> </div> <p>The array literal.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nb">Array</span><span class="o">:</span> <span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;[ ArgList OptComma ]&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ArrayNode</span> <span class="nx">$2</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-49"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-49">#</a> </div> <p>The <strong>ArgList</strong> is both the list of objects passed into a function call,
as well as the contents of an array literal
(i.e. comma-separated expressions). Newlines work as well.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ArgList: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[]</span>
<span class="nx">o</span> <span class="s2">&quot;Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nx">$1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;INDENT Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nx">$2</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ArgList , Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[</span><span class="nx">$3</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ArgList TERMINATOR Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[</span><span class="nx">$3</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ArgList , TERMINATOR Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[</span><span class="nx">$4</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ArgList , INDENT Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[</span><span class="nx">$4</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ArgList OptComma OUTDENT&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-50"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-50">#</a> </div> <p>Just simple, comma-separated, required arguments (no fancy syntax). We need
this to be separate from the <strong>ArgList</strong> for use in <strong>Switch</strong> blocks, where
having the newlines wouldn't make sense.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">SimpleArgs: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Expression&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;SimpleArgs , Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span>
<span class="k">if</span> <span class="nx">$1</span> <span class="k">instanceof</span> <span class="nb">Array</span> <span class="k">then</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">concat</span><span class="p">([</span><span class="nx">$3</span><span class="p">])</span> <span class="k">else</span> <span class="p">[</span><span class="nx">$1</span><span class="p">].</span><span class="nx">concat</span><span class="p">([</span><span class="nx">$3</span><span class="p">])</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-51"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-51">#</a> </div> <p>The variants of <em>try/catch/finally</em> exception handling blocks.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Try: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;TRY Block Catch&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">TryNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$3</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">$3</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;TRY Block FINALLY Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">TryNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="nx">$4</span>
<span class="nx">o</span> <span class="s2">&quot;TRY Block Catch FINALLY Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">TryNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$3</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">$3</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="nx">$5</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-52"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-52">#</a> </div> <p>A catch clause names its error and runs a block of code.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Catch: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;CATCH Identifier Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nx">$2</span><span class="p">,</span> <span class="nx">$3</span><span class="p">]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-53"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-53">#</a> </div> <p>Throw an exception object.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Throw: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;THROW Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ThrowNode</span> <span class="nx">$2</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-54"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-54">#</a> </div> <p>Parenthetical expressions. Note that the <strong>Parenthetical</strong> is a <strong>Value</strong>,
not an <strong>Expression</strong>, so if you need to use an expression in a place
where only values are accepted, wrapping it in parentheses will always do
the trick.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Parenthetical: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;( Line )&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ParentheticalNode</span> <span class="nx">$2</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-55"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-55">#</a> </div> <p>A language extension to CoffeeScript from the outside. We simply pass
it through unaltered.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Extension: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;EXTENSION&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-56"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-56">#</a> </div> <p>The condition portion of a while loop.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">WhileSource: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;WHILE Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">WhileNode</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;WHILE Expression WHEN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">WhileNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="p">{</span><span class="nv">guard : </span><span class="nx">$4</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;UNTIL Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">WhileNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="p">{</span><span class="nv">invert: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;UNTIL Expression WHEN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">WhileNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="p">{</span><span class="nv">invert: </span><span class="kc">true</span><span class="p">,</span> <span class="nv">guard: </span><span class="nx">$4</span><span class="p">}</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-57"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-57">#</a> </div> <p>The while loop can either be normal, with a block of expressions to execute,
or postfix, with a single expression. There is no do..while.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">While: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;WhileSource Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">add_body</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;Statement WhileSource&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$2</span><span class="p">.</span><span class="nx">add_body</span> <span class="nx">Expressions</span><span class="p">.</span><span class="nx">wrap</span> <span class="p">[</span><span class="nx">$1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;Expression WhileSource&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$2</span><span class="p">.</span><span class="nx">add_body</span> <span class="nx">Expressions</span><span class="p">.</span><span class="nx">wrap</span> <span class="p">[</span><span class="nx">$1</span><span class="p">]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-58"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-58">#</a> </div> <p>Array, object, and range comprehensions, at the most generic level.
Comprehensions can either be normal, with a block of expressions to execute,
or postfix, with a single expression.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">For: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Statement FOR ForVariables ForSource&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ForNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$4</span><span class="p">,</span> <span class="nx">$3</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">$3</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;Expression FOR ForVariables ForSource&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ForNode</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$4</span><span class="p">,</span> <span class="nx">$3</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">$3</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;FOR ForVariables ForSource Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ForNode</span> <span class="nx">$4</span><span class="p">,</span> <span class="nx">$3</span><span class="p">,</span> <span class="nx">$2</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">$2</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-59"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-59">#</a> </div> <p>An array of all accepted values for a variable inside the loop. This
enables support for pattern matching.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ForValue: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;Identifier&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Array&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="nx">o</span> <span class="s2">&quot;Object&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">ValueNode</span> <span class="nx">$1</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-60"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-60">#</a> </div> <p>An array or range comprehension has variables for the current element and
(optional) reference to the current index. Or, <em>key, value</em>, in the case
of object comprehensions.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ForVariables: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;ForValue&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nx">$1</span><span class="p">]</span>
<span class="nx">o</span> <span class="s2">&quot;ForValue , ForValue&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span><span class="p">]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-61"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-61">#</a> </div> <p>The source of a comprehension is an array or object with an optional guard
clause. If it's an array comprehension, you can also choose to step through
in fixed-size increments.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ForSource: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;IN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="nv">source: </span><span class="nx">$2</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;OF Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="nv">source: </span><span class="nx">$2</span><span class="p">,</span> <span class="nv">object: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;IN Expression WHEN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="nv">source: </span><span class="nx">$2</span><span class="p">,</span> <span class="nv">guard: </span><span class="nx">$4</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;OF Expression WHEN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="nv">source: </span><span class="nx">$2</span><span class="p">,</span> <span class="nv">guard: </span><span class="nx">$4</span><span class="p">,</span> <span class="nv">object: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;IN Expression BY Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="nv">source: </span><span class="nx">$2</span><span class="p">,</span> <span class="nv">step: </span> <span class="nx">$4</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;IN Expression WHEN Expression BY Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="nv">source: </span><span class="nx">$2</span><span class="p">,</span> <span class="nv">guard: </span><span class="nx">$4</span><span class="p">,</span> <span class="nv">step: </span> <span class="nx">$6</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;IN Expression BY Expression WHEN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="nv">source: </span><span class="nx">$2</span><span class="p">,</span> <span class="nv">step: </span> <span class="nx">$4</span><span class="p">,</span> <span class="nv">guard: </span><span class="nx">$6</span><span class="p">}</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-62"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-62">#</a> </div> <p>The CoffeeScript switch/when/else block replaces the JavaScript
switch/case/default by compiling into an if-else chain.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Switch: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;SWITCH Expression INDENT Whens OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$4</span><span class="p">.</span><span class="nx">switches_over</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;SWITCH Expression INDENT Whens ELSE Block OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$4</span><span class="p">.</span><span class="nx">switches_over</span><span class="p">(</span><span class="nx">$2</span><span class="p">).</span><span class="nx">add_else</span> <span class="nx">$6</span><span class="p">,</span> <span class="kc">true</span>
<span class="nx">o</span> <span class="s2">&quot;SWITCH INDENT Whens OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;SWITCH INDENT Whens ELSE Block OUTDENT&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$3</span><span class="p">.</span><span class="nx">add_else</span> <span class="nx">$5</span><span class="p">,</span> <span class="kc">true</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-63"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-63">#</a> </div> <p>The inner list of whens is left recursive. At code-generation time, the
IfNode will rewrite them into a proper chain.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Whens: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;When&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Whens When&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">add_else</span> <span class="nx">$2</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-64"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-64">#</a> </div> <p>An individual <strong>When</strong> clause, with action.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">When: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;LEADING_WHEN SimpleArgs Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IfNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$3</span><span class="p">,</span> <span class="p">{</span><span class="nv">statement: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;LEADING_WHEN SimpleArgs Block TERMINATOR&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IfNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$3</span><span class="p">,</span> <span class="p">{</span><span class="nv">statement: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;Comment TERMINATOR When&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nv">$3.comment: </span><span class="nx">$1</span><span class="p">;</span> <span class="nx">$3</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-65"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-65">#</a> </div> <p>The most basic form of <em>if</em> is a condition and an action. The following
if-related rules are broken up along these lines in order to avoid
ambiguity.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">IfStart: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;IF Expression Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IfNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;UNLESS Expression Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IfNode</span> <span class="nx">$2</span><span class="p">,</span> <span class="nx">$3</span><span class="p">,</span> <span class="p">{</span><span class="nv">invert: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;IfStart ElsIf&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">add_else</span> <span class="nx">$2</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-66"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-66">#</a> </div> <p>An <strong>IfStart</strong> can optionally be followed by an else block.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">IfBlock: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;IfStart&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;IfStart ELSE Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">$1</span><span class="p">.</span><span class="nx">add_else</span> <span class="nx">$3</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-67"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-67">#</a> </div> <p>An <em>else if</em> continuation of the <em>if</em> expression.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ElsIf: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;ELSE IF Expression Block&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="k">new</span> <span class="nx">IfNode</span><span class="p">(</span><span class="nx">$3</span><span class="p">,</span> <span class="nx">$4</span><span class="p">)).</span><span class="nx">force_statement</span><span class="p">()</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-68"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-68">#</a> </div> <p>The full complement of <em>if</em> expressions, including postfix one-liner
<em>if</em> and <em>unless</em>.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">If: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;IfBlock&quot;</span>
<span class="nx">o</span> <span class="s2">&quot;Statement IF Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IfNode</span> <span class="nx">$3</span><span class="p">,</span> <span class="nx">Expressions</span><span class="p">.</span><span class="nx">wrap</span><span class="p">([</span><span class="nx">$1</span><span class="p">]),</span> <span class="p">{</span><span class="nv">statement: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;Expression IF Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IfNode</span> <span class="nx">$3</span><span class="p">,</span> <span class="nx">Expressions</span><span class="p">.</span><span class="nx">wrap</span><span class="p">([</span><span class="nx">$1</span><span class="p">]),</span> <span class="p">{</span><span class="nv">statement: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;Statement UNLESS Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IfNode</span> <span class="nx">$3</span><span class="p">,</span> <span class="nx">Expressions</span><span class="p">.</span><span class="nx">wrap</span><span class="p">([</span><span class="nx">$1</span><span class="p">]),</span> <span class="p">{</span><span class="nv">statement: </span><span class="kc">true</span><span class="p">,</span> <span class="nv">invert: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">o</span> <span class="s2">&quot;Expression UNLESS Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">IfNode</span> <span class="nx">$3</span><span class="p">,</span> <span class="nx">Expressions</span><span class="p">.</span><span class="nx">wrap</span><span class="p">([</span><span class="nx">$1</span><span class="p">]),</span> <span class="p">{</span><span class="nv">statement: </span><span class="kc">true</span><span class="p">,</span> <span class="nv">invert: </span><span class="kc">true</span><span class="p">}</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-69"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-69">#</a> </div> <p>Arithmetic and logical operators, working on one or more operands.
Here they are grouped by order of precedence. The actual precedence rules
are defined at the bottom of the page. It would be shorter if we could
combine most of these rules into a single generic <em>Operand OpSymbol Operand</em>
-type rule, but in order to make the precedence binding possible, separate
rules are necessary.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">Operation: </span><span class="p">[</span>
<span class="nx">o</span> <span class="s2">&quot;! Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;!&#39;</span><span class="p">,</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;!! Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;!!&#39;</span><span class="p">,</span> <span class="nx">$2</span>
<span class="nx">o</span><span class="p">(</span><span class="s2">&quot;- Expression&quot;</span><span class="p">,</span> <span class="p">(</span><span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span><span class="p">(</span><span class="s1">&#39;-&#39;</span><span class="p">,</span> <span class="nx">$2</span><span class="p">)),</span> <span class="p">{</span><span class="nv">prec: </span><span class="s1">&#39;UMINUS&#39;</span><span class="p">})</span>
<span class="nx">o</span><span class="p">(</span><span class="s2">&quot;+ Expression&quot;</span><span class="p">,</span> <span class="p">(</span><span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span><span class="p">(</span><span class="s1">&#39;+&#39;</span><span class="p">,</span> <span class="nx">$2</span><span class="p">)),</span> <span class="p">{</span><span class="nv">prec: </span><span class="s1">&#39;UPLUS&#39;</span><span class="p">})</span>
<span class="nx">o</span> <span class="s2">&quot;~ Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;~&#39;</span><span class="p">,</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;-- Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;--&#39;</span><span class="p">,</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;++ Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;++&#39;</span><span class="p">,</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;DELETE Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;delete&#39;</span><span class="p">,</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;TYPEOF Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;typeof&#39;</span><span class="p">,</span> <span class="nx">$2</span>
<span class="nx">o</span> <span class="s2">&quot;Expression --&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;--&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">true</span>
<span class="nx">o</span> <span class="s2">&quot;Expression ++&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;++&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">true</span>
<span class="nx">o</span> <span class="s2">&quot;Expression * Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;*&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression / Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression % Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;%&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression + Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;+&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression - Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;-&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &lt;&lt; Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&lt;&lt;&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &gt;&gt; Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&gt;&gt;&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &gt;&gt;&gt; Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&gt;&gt;&gt;&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &amp; Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&amp;&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression | Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;|&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression ^ Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;^&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &lt;= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&lt;=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &lt; Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&lt;&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &gt; Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&gt;&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &gt;= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&gt;=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression == Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;==&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression != Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;!=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &amp;&amp; Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&amp;&amp;&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression || Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;||&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression ? Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;?&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression -= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;-=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression += Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;+=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression /= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;/=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression *= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;*=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression %= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;%=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression ||= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;||=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression &amp;&amp;= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;&amp;&amp;=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression ?= Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;?=&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression INSTANCEOF Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;instanceof&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="nx">o</span> <span class="s2">&quot;Expression IN Expression&quot;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nx">OpNode</span> <span class="s1">&#39;in&#39;</span><span class="p">,</span> <span class="nx">$1</span><span class="p">,</span> <span class="nx">$3</span>
<span class="p">]</span>
<span class="p">}</span></pre></div> </td> </tr> <tr id="section-70"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-70">#</a> </div> <h2>Precedence</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-71"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-71">#</a> </div> <p>Operators at the top of this list have higher precedence than the ones lower
down. Following these rules is what makes <code>2 + 3 * 4</code> parse as:</p>
<pre><code>2 + (3 * 4)
</code></pre>
<p>And not:</p>
<pre><code>(2 + 3) * 4
</code></pre> </td> <td class="code"> <div class="highlight"><pre><span class="nv">operators: </span><span class="p">[</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;?&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;nonassoc&quot;</span><span class="p">,</span> <span class="s1">&#39;UMINUS&#39;</span><span class="p">,</span> <span class="s1">&#39;UPLUS&#39;</span><span class="p">,</span> <span class="s1">&#39;!&#39;</span><span class="p">,</span> <span class="s1">&#39;!!&#39;</span><span class="p">,</span> <span class="s1">&#39;~&#39;</span><span class="p">,</span> <span class="s1">&#39;++&#39;</span><span class="p">,</span> <span class="s1">&#39;--&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;*&#39;</span><span class="p">,</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="s1">&#39;%&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;+&#39;</span><span class="p">,</span> <span class="s1">&#39;-&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;&lt;&lt;&#39;</span><span class="p">,</span> <span class="s1">&#39;&gt;&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;&gt;&gt;&gt;&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;&amp;&#39;</span><span class="p">,</span> <span class="s1">&#39;|&#39;</span><span class="p">,</span> <span class="s1">&#39;^&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;&lt;=&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt;&#39;</span><span class="p">,</span> <span class="s1">&#39;&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;&gt;=&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;right&quot;</span><span class="p">,</span> <span class="s1">&#39;DELETE&#39;</span><span class="p">,</span> <span class="s1">&#39;INSTANCEOF&#39;</span><span class="p">,</span> <span class="s1">&#39;TYPEOF&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;==&#39;</span><span class="p">,</span> <span class="s1">&#39;!=&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;&amp;&amp;&#39;</span><span class="p">,</span> <span class="s1">&#39;||&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;right&quot;</span><span class="p">,</span> <span class="s1">&#39;-=&#39;</span><span class="p">,</span> <span class="s1">&#39;+=&#39;</span><span class="p">,</span> <span class="s1">&#39;/=&#39;</span><span class="p">,</span> <span class="s1">&#39;*=&#39;</span><span class="p">,</span> <span class="s1">&#39;%=&#39;</span><span class="p">,</span> <span class="s1">&#39;||=&#39;</span><span class="p">,</span> <span class="s1">&#39;&amp;&amp;=&#39;</span><span class="p">,</span> <span class="s1">&#39;?=&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;.&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;right&quot;</span><span class="p">,</span> <span class="s1">&#39;INDENT&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;OUTDENT&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;right&quot;</span><span class="p">,</span> <span class="s1">&#39;WHEN&#39;</span><span class="p">,</span> <span class="s1">&#39;LEADING_WHEN&#39;</span><span class="p">,</span> <span class="s1">&#39;IN&#39;</span><span class="p">,</span> <span class="s1">&#39;OF&#39;</span><span class="p">,</span> <span class="s1">&#39;BY&#39;</span><span class="p">,</span> <span class="s1">&#39;THROW&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;right&quot;</span><span class="p">,</span> <span class="s1">&#39;FOR&#39;</span><span class="p">,</span> <span class="s1">&#39;WHILE&#39;</span><span class="p">,</span> <span class="s1">&#39;UNTIL&#39;</span><span class="p">,</span> <span class="s1">&#39;NEW&#39;</span><span class="p">,</span> <span class="s1">&#39;SUPER&#39;</span><span class="p">,</span> <span class="s1">&#39;CLASS&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;left&quot;</span><span class="p">,</span> <span class="s1">&#39;EXTENDS&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;right&quot;</span><span class="p">,</span> <span class="s1">&#39;ASSIGN&#39;</span><span class="p">,</span> <span class="s1">&#39;RETURN&#39;</span><span class="p">]</span>
<span class="p">[</span><span class="s2">&quot;right&quot;</span><span class="p">,</span> <span class="s1">&#39;-&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;=&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt;-&#39;</span><span class="p">,</span> <span class="s1">&#39;UNLESS&#39;</span><span class="p">,</span> <span class="s1">&#39;IF&#39;</span><span class="p">,</span> <span class="s1">&#39;ELSE&#39;</span><span class="p">]</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-72"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-72">#</a> </div> <h2>Wrapping Up</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-73"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-73">#</a> </div> <p>Finally, now what we have our <strong>grammar</strong> and our <strong>operators</strong>, we can create
our <strong>Jison.Parser</strong>. We do this by processing all of our rules, recording all
terminals (every symbol which does not appear as the name of a rule above)
as "tokens".</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">tokens: </span><span class="p">[]</span>
<span class="k">for</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">alternatives</span> <span class="k">of</span> <span class="nx">grammar</span>
<span class="nx">grammar</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span><span class="o">:</span> <span class="k">for</span> <span class="nx">alt</span> <span class="k">in</span> <span class="nx">alternatives</span>
<span class="k">for</span> <span class="nx">token</span> <span class="k">in</span> <span class="nx">alt</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">split</span> <span class="s1">&#39; &#39;</span>
<span class="nx">tokens</span><span class="p">.</span><span class="nx">push</span> <span class="nx">token</span> <span class="nx">unless</span> <span class="nx">grammar</span><span class="p">[</span><span class="nx">token</span><span class="p">]</span>
<span class="nx">alt</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;return ${alt[1]}&quot;</span> <span class="k">if</span> <span class="nx">name</span> <span class="o">is</span> <span class="s1">&#39;Root&#39;</span>
<span class="nx">alt</span></pre></div> </td> </tr> <tr id="section-74"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-74">#</a> </div> <p>Initialize the <strong>Parser</strong> with our list of terminal <strong>tokens</strong>, our <strong>grammar</strong>
rules, and the name of the root. Reverse the operators because Jison orders
precedence from low to high, and we have it high to low
(as in <a href="http://dinosaur.compilertools.net/yacc/index.html">Yacc</a>).</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.parser: </span><span class="k">new</span> <span class="nx">Parser</span> <span class="p">{</span>
<span class="nv">tokens: </span> <span class="nx">tokens</span><span class="p">.</span><span class="nx">join</span> <span class="s1">&#39; &#39;</span>
<span class="nv">bnf: </span> <span class="nx">grammar</span>
<span class="nv">operators: </span> <span class="nx">operators</span><span class="p">.</span><span class="nx">reverse</span><span class="p">()</span>
<span class="nv">startSymbol: </span> <span class="s1">&#39;Root&#39;</span>
<span class="p">}</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,64 @@
<!DOCTYPE html> <html> <head> <title>helpers.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> helpers.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>This file contains the common helper functions that we'd like to share among
the <strong>Lexer</strong>, <strong>Rewriter</strong>, and the <strong>Nodes</strong>. Merge objects, flatten
arrays, count characters, that sort of thing.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>Set up exported variables for both <strong>Node.js</strong> and the browser.</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">this</span><span class="p">.</span><span class="nv">exports: </span><span class="k">this</span> <span class="nx">unless</span> <span class="nx">process</span><span class="o">?</span>
<span class="nv">helpers: exports.helpers: </span><span class="p">{}</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>Cross-browser indexOf, so that IE can join the party.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.index_of: index_of: </span><span class="p">(</span><span class="nx">array</span><span class="p">,</span> <span class="nx">item</span><span class="p">,</span> <span class="nx">from</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">return</span> <span class="nx">array</span><span class="p">.</span><span class="nx">indexOf</span> <span class="nx">item</span><span class="p">,</span> <span class="nx">from</span> <span class="k">if</span> <span class="nx">array</span><span class="p">.</span><span class="nx">indexOf</span>
<span class="k">for</span> <span class="nx">other</span><span class="p">,</span> <span class="nx">index</span> <span class="k">in</span> <span class="nx">array</span>
<span class="k">if</span> <span class="nx">other</span> <span class="o">is</span> <span class="nx">item</span> <span class="o">and</span> <span class="p">(</span><span class="o">not</span> <span class="nx">from</span> <span class="o">or</span> <span class="p">(</span><span class="nx">from</span> <span class="o">&lt;=</span> <span class="nx">index</span><span class="p">))</span>
<span class="k">return</span> <span class="nx">index</span>
<span class="o">-</span><span class="mi">1</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>Does a list include a value?</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.include: include: </span><span class="p">(</span><span class="nx">list</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">index_of</span><span class="p">(</span><span class="nx">list</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">0</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>Peek at the beginning of a given string to see if it matches a sequence.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.starts: starts: </span><span class="p">(</span><span class="nx">string</span><span class="p">,</span> <span class="nx">literal</span><span class="p">,</span> <span class="nx">start</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">string</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="nx">start</span><span class="p">,</span> <span class="p">(</span><span class="nx">start</span> <span class="o">or</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="nx">literal</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="o">is</span> <span class="nx">literal</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p>Trim out all falsy values from an array.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.compact: compact: </span><span class="p">(</span><span class="nx">array</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nx">item</span> <span class="k">for</span> <span class="nx">item</span> <span class="k">in</span> <span class="nx">array</span> <span class="k">when</span> <span class="nx">item</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>Count the number of occurences of a character in a string.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.count: count: </span><span class="p">(</span><span class="nx">string</span><span class="p">,</span> <span class="nx">letter</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">num: </span><span class="mi">0</span>
<span class="nv">pos: </span><span class="nx">index_of</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">letter</span>
<span class="k">while</span> <span class="nx">pos</span> <span class="o">isnt</span> <span class="o">-</span><span class="mi">1</span>
<span class="nv">num: </span><span class="o">+</span> <span class="mi">1</span>
<span class="nv">pos: </span><span class="nx">index_of</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">letter</span><span class="p">,</span> <span class="nx">pos</span> <span class="o">+</span> <span class="mi">1</span>
<span class="nx">num</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>Merge objects, returning a fresh copy with attributes from both sides.
Used every time <code>BaseNode#compile</code> is called, to allow properties in the
options hash to propagate down the tree without polluting other branches.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.merge: merge: </span><span class="p">(</span><span class="nx">options</span><span class="p">,</span> <span class="nx">overrides</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">fresh: </span><span class="p">{}</span>
<span class="p">(</span><span class="nx">fresh</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span><span class="o">:</span> <span class="nx">val</span><span class="p">)</span> <span class="k">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">val</span> <span class="k">of</span> <span class="nx">options</span>
<span class="p">(</span><span class="nx">fresh</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span><span class="o">:</span> <span class="nx">val</span><span class="p">)</span> <span class="k">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">val</span> <span class="k">of</span> <span class="nx">overrides</span> <span class="k">if</span> <span class="nx">overrides</span>
<span class="nx">fresh</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>Extend a source object with the properties of another object (shallow copy).
We use this to simulate Node's deprecated <code>process.mixin</code></p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.extend: extend: </span><span class="p">(</span><span class="nx">object</span><span class="p">,</span> <span class="nx">properties</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="p">(</span><span class="nx">object</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span><span class="o">:</span> <span class="nx">val</span><span class="p">)</span> <span class="k">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">val</span> <span class="k">of</span> <span class="nx">properties</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>Return a completely flattened version of an array. Handy for getting a
list of <code>children</code> from the nodes.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.flatten: flatten: </span><span class="p">(</span><span class="nx">array</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">memo: </span><span class="p">[]</span>
<span class="k">for</span> <span class="nx">item</span> <span class="k">in</span> <span class="nx">array</span>
<span class="k">if</span> <span class="nx">item</span> <span class="k">instanceof</span> <span class="nb">Array</span> <span class="k">then</span> <span class="nv">memo: </span><span class="nx">memo</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="k">else</span> <span class="nx">memo</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span>
<span class="nx">memo</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-11">#</a> </div> <p>Delete a key from an object, returning the value. Useful when a node is
looking for a particular method in an options hash.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.del: del: </span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">key</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">val: </span><span class="nx">obj</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span>
<span class="k">delete</span> <span class="nx">obj</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span>
<span class="nx">val</span></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-12">#</a> </div> <p>Matches a balanced group such as a single or double-quoted string. Pass in
a series of delimiters, all of which must be nested correctly within the
contents of the string. This method allows us to have strings within
interpolations within strings, ad infinitum.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">helpers.balanced_string: balanced_string: </span><span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="nx">delimited</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">options: </span><span class="o">or</span> <span class="p">{}</span>
<span class="nv">slash: </span><span class="nx">delimited</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;/&#39;</span>
<span class="nv">levels: </span><span class="p">[]</span>
<span class="nv">i: </span><span class="mi">0</span>
<span class="k">while</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">str</span><span class="p">.</span><span class="nx">length</span>
<span class="k">if</span> <span class="nx">levels</span><span class="p">.</span><span class="nx">length</span> <span class="o">and</span> <span class="nx">starts</span> <span class="nx">str</span><span class="p">,</span> <span class="s1">&#39;\\&#39;</span><span class="p">,</span> <span class="nx">i</span>
<span class="nv">i: </span><span class="o">+</span> <span class="mi">1</span>
<span class="k">else</span>
<span class="k">for</span> <span class="nx">pair</span> <span class="k">in</span> <span class="nx">delimited</span>
<span class="p">[</span><span class="nx">open</span><span class="p">,</span> <span class="nx">close</span><span class="p">]</span><span class="o">:</span> <span class="nx">pair</span>
<span class="k">if</span> <span class="nx">levels</span><span class="p">.</span><span class="nx">length</span> <span class="o">and</span> <span class="nx">starts</span><span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="nx">close</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">and</span> <span class="nx">levels</span><span class="p">[</span><span class="nx">levels</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">is</span> <span class="nx">pair</span>
<span class="nx">levels</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="nv">i: </span><span class="o">+</span> <span class="nx">close</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span>
<span class="nv">i: </span><span class="o">+</span> <span class="mi">1</span> <span class="nx">unless</span> <span class="nx">levels</span><span class="p">.</span><span class="nx">length</span>
<span class="k">break</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">starts</span> <span class="nx">str</span><span class="p">,</span> <span class="nx">open</span><span class="p">,</span> <span class="nx">i</span>
<span class="nx">levels</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">pair</span><span class="p">)</span>
<span class="nv">i: </span><span class="o">+</span> <span class="nx">open</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">break</span>
<span class="k">break</span> <span class="k">if</span> <span class="o">not</span> <span class="nx">levels</span><span class="p">.</span><span class="nx">length</span> <span class="o">or</span> <span class="nx">slash</span> <span class="o">and</span> <span class="nx">starts</span> <span class="nx">str</span><span class="p">,</span> <span class="s1">&#39;\n&#39;</span><span class="p">,</span> <span class="nx">i</span>
<span class="nv">i: </span><span class="o">+</span> <span class="mi">1</span>
<span class="k">if</span> <span class="nx">levels</span><span class="p">.</span><span class="nx">length</span>
<span class="k">return</span> <span class="kc">false</span> <span class="k">if</span> <span class="nx">slash</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span> <span class="s2">&quot;SyntaxError: Unterminated ${levels.pop()[0]} starting on line ${@line + 1}&quot;</span>
<span class="k">if</span> <span class="o">not</span> <span class="nx">i</span> <span class="k">then</span> <span class="kc">false</span> <span class="k">else</span> <span class="nx">str</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,3 @@
<!DOCTYPE html> <html> <head> <title>index.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> index.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>Loader for CoffeeScript as a Node.js library.</p> </td> <td class="code"> <div class="highlight"><pre><span class="p">(</span><span class="nx">exports</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span><span class="o">:</span> <span class="nx">val</span><span class="p">)</span> <span class="k">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">val</span> <span class="k">of</span> <span class="nx">require</span> <span class="s1">&#39;./coffee-script&#39;</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,383 @@
<!DOCTYPE html> <html> <head> <title>lexer.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> lexer.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
matches against the beginning of the source code. When a match is found,
a token is produced, we consume the match, and start again. Tokens are in the
form:</p>
<pre><code>[tag, value, line_number]
</code></pre>
<p>Which is a format that can be fed directly into <a href="http://github.com/zaach/jison">Jison</a>.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>Set up the Lexer for both Node.js and the browser, depending on where we are.</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">if</span> <span class="nx">process</span><span class="o">?</span>
<span class="p">{</span><span class="nx">Rewriter</span><span class="p">}</span><span class="o">:</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./rewriter&#39;</span><span class="p">)</span>
<span class="p">{</span><span class="nx">helpers</span><span class="p">}</span><span class="o">:</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./helpers&#39;</span><span class="p">)</span>
<span class="k">else</span>
<span class="k">this</span><span class="p">.</span><span class="nv">exports: </span><span class="k">this</span>
<span class="nv">Rewriter: </span> <span class="k">this</span><span class="p">.</span><span class="nx">Rewriter</span>
<span class="nv">helpers: </span> <span class="k">this</span><span class="p">.</span><span class="nx">helpers</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>Import the helpers we need.</p> </td> <td class="code"> <div class="highlight"><pre><span class="p">{</span><span class="nx">include</span><span class="p">,</span> <span class="nx">count</span><span class="p">,</span> <span class="nx">starts</span><span class="p">,</span> <span class="nx">compact</span><span class="p">,</span> <span class="nx">balanced_string</span><span class="p">}</span><span class="o">:</span> <span class="nx">helpers</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <h2>The Lexer Class</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>The Lexer class reads a stream of CoffeeScript and divvys it up into tagged
tokens. Some potential ambiguity in the grammar has been avoided by
pushing some extra smarts into the Lexer.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.Lexer: </span><span class="nx">class</span> <span class="nx">Lexer</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p><strong>tokenize</strong> is the Lexer's main method. Scan by attempting to match tokens
one at a time, using a regular expression anchored at the start of the
remaining code, or a custom recursive token-matching method
(for interpolations). When the next token has been recorded, we move forward
within the code past the token, and begin again.</p>
<p>Each tokenizing method is responsible for incrementing <code>@i</code> by the number of
characters it has consumed. <code>@i</code> can be thought of as our finger on the page
of source.</p>
<p>Before returning the token stream, run it through the <a href="rewriter.html">Rewriter</a>
unless explicitly asked not to.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">tokenize: </span><span class="p">(</span><span class="nx">code</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">code : </span><span class="nx">code</span><span class="p">.</span><span class="nx">replace</span> <span class="sr">/(\r|\s+$)/g</span><span class="p">,</span> <span class="s1">&#39;&#39;</span>
<span class="nv">o : </span><span class="nx">options</span> <span class="o">or</span> <span class="p">{}</span>
<span class="vi">@code : </span><span class="nx">code</span> <span class="c1"># The remainder of the source code.</span>
<span class="vi">@i : </span><span class="mi">0</span> <span class="c1"># Current character position we&#39;re parsing.</span>
<span class="vi">@line : </span><span class="nx">o</span><span class="p">.</span><span class="nx">line</span> <span class="o">or</span> <span class="mi">0</span> <span class="c1"># The current line.</span>
<span class="vi">@indent : </span><span class="mi">0</span> <span class="c1"># The current indentation level.</span>
<span class="vi">@indents : </span><span class="p">[]</span> <span class="c1"># The stack of all current indentation levels.</span>
<span class="vi">@tokens : </span><span class="p">[]</span> <span class="c1"># Stream of parsed tokens in the form [&#39;TYPE&#39;, value, line]</span>
<span class="k">while</span> <span class="nx">@i</span> <span class="o">&lt;</span> <span class="nx">@code</span><span class="p">.</span><span class="nx">length</span>
<span class="vi">@chunk: </span><span class="nx">@code</span><span class="p">.</span><span class="nx">slice</span> <span class="nx">@i</span>
<span class="nx">@extract_next_token</span><span class="p">()</span>
<span class="nx">@close_indentation</span><span class="p">()</span>
<span class="k">return</span> <span class="nx">@tokens</span> <span class="k">if</span> <span class="nx">o</span><span class="p">.</span><span class="nx">rewrite</span> <span class="o">is</span> <span class="kc">off</span>
<span class="p">(</span><span class="k">new</span> <span class="nx">Rewriter</span><span class="p">()).</span><span class="nx">rewrite</span> <span class="nx">@tokens</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>At every position, run through this list of attempted matches,
short-circuiting if any of them succeed. Their order determines precedence:
<code>@literal_token</code> is the fallback catch-all.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">extract_next_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@extension_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@identifier_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@number_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@heredoc_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@regex_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@comment_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@line_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@whitespace_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@js_token</span><span class="p">()</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@string_token</span><span class="p">()</span>
<span class="k">return</span> <span class="nx">@literal_token</span><span class="p">()</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <h2>Tokenizers</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>Language extensions get the highest priority, first chance to tag tokens
as something else.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">extension_token: </span><span class="o">-&gt;</span>
<span class="k">for</span> <span class="nx">extension</span> <span class="k">in</span> <span class="nx">Lexer</span><span class="p">.</span><span class="nx">extensions</span>
<span class="k">return</span> <span class="kc">true</span> <span class="k">if</span> <span class="nx">extension</span><span class="p">.</span><span class="nx">call</span> <span class="k">this</span>
<span class="kc">false</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>Matches identifying literals: variables, keywords, method names, etc.
Check to ensure that JavaScript reserved words aren't being used as
identifiers. Because CoffeeScript reserves a handful of keywords that are
allowed in JavaScript, we're careful not to tag them as keywords when
referenced as property names here, so you can still do <code>jQuery.is()</code> even
though <code>is</code> means <code>===</code> otherwise.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">identifier_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">id: </span><span class="nx">@match</span> <span class="nx">IDENTIFIER</span><span class="p">,</span> <span class="mi">1</span>
<span class="nx">@name_access_type</span><span class="p">()</span>
<span class="nv">accessed: </span><span class="nx">include</span> <span class="nx">ACCESSORS</span><span class="p">,</span> <span class="nx">@tag</span> <span class="mi">0</span>
<span class="nv">tag: </span><span class="s1">&#39;IDENTIFIER&#39;</span>
<span class="nv">tag: </span><span class="nx">id</span><span class="p">.</span><span class="nx">toUpperCase</span><span class="p">()</span> <span class="k">if</span> <span class="o">not</span> <span class="nx">accessed</span> <span class="o">and</span> <span class="nx">include</span><span class="p">(</span><span class="nx">KEYWORDS</span><span class="p">,</span> <span class="nx">id</span><span class="p">)</span>
<span class="nx">@identifier_error</span> <span class="nx">id</span> <span class="k">if</span> <span class="nx">include</span> <span class="nx">RESERVED</span><span class="p">,</span> <span class="nx">id</span>
<span class="nv">tag: </span><span class="s1">&#39;LEADING_WHEN&#39;</span> <span class="k">if</span> <span class="nx">tag</span> <span class="o">is</span> <span class="s1">&#39;WHEN&#39;</span> <span class="o">and</span> <span class="nx">include</span> <span class="nx">LINE_BREAK</span><span class="p">,</span> <span class="nx">@tag</span><span class="p">()</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">id</span><span class="p">.</span><span class="nx">length</span>
<span class="nx">unless</span> <span class="nx">accessed</span>
<span class="nv">tag: id: </span><span class="nx">CONVERSIONS</span><span class="p">[</span><span class="nx">id</span><span class="p">]</span> <span class="k">if</span> <span class="nx">include</span> <span class="nx">COFFEE_ALIASES</span><span class="p">,</span> <span class="nx">id</span>
<span class="k">return</span> <span class="nx">@tag_half_assignment</span> <span class="nx">tag</span> <span class="k">if</span> <span class="nx">@prev</span><span class="p">()</span> <span class="o">and</span> <span class="nx">@prev</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;ASSIGN&#39;</span> <span class="o">and</span> <span class="nx">include</span> <span class="nx">HALF_ASSIGNMENTS</span><span class="p">,</span> <span class="nx">tag</span>
<span class="nx">@token</span> <span class="nx">tag</span><span class="p">,</span> <span class="nx">id</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-11">#</a> </div> <p>Matches numbers, including decimals, hex, and exponential notation.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">number_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">number: </span><span class="nx">@match</span> <span class="nx">NUMBER</span><span class="p">,</span> <span class="mi">1</span>
<span class="nx">@token</span> <span class="s1">&#39;NUMBER&#39;</span><span class="p">,</span> <span class="nx">number</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">number</span><span class="p">.</span><span class="nx">length</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-12">#</a> </div> <p>Matches strings, including multi-line strings. Ensures that quotation marks
are balanced within the string's contents, and within nested interpolations.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">string_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nx">starts</span><span class="p">(</span><span class="nx">@chunk</span><span class="p">,</span> <span class="s1">&#39;&quot;&#39;</span><span class="p">)</span> <span class="o">or</span> <span class="nx">starts</span><span class="p">(</span><span class="nx">@chunk</span><span class="p">,</span> <span class="s2">&quot;&#39;&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">string:</span>
<span class="nx">@balanced_token</span><span class="p">([</span><span class="s1">&#39;&quot;&#39;</span><span class="p">,</span> <span class="s1">&#39;&quot;&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;${&#39;</span><span class="p">,</span> <span class="s1">&#39;}&#39;</span><span class="p">])</span> <span class="o">or</span>
<span class="nx">@balanced_token</span> <span class="p">[</span><span class="s2">&quot;&#39;&quot;</span><span class="p">,</span> <span class="s2">&quot;&#39;&quot;</span><span class="p">]</span>
<span class="nx">@interpolate_string</span> <span class="nx">string</span><span class="p">.</span><span class="nx">replace</span> <span class="nx">STRING_NEWLINES</span><span class="p">,</span> <span class="s2">&quot; \\\n&quot;</span>
<span class="vi">@line: </span><span class="o">+</span> <span class="nx">count</span> <span class="nx">string</span><span class="p">,</span> <span class="s2">&quot;\n&quot;</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">string</span><span class="p">.</span><span class="nx">length</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-13">#</a> </div> <p>Matches heredocs, adjusting indentation to the correct level, as heredocs
preserve whitespace, but ignore indentation to the left.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">heredoc_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">match: </span><span class="nx">@chunk</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">HEREDOC</span><span class="p">)</span>
<span class="nv">quote: </span><span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">substr</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span>
<span class="nv">doc: </span><span class="nx">@sanitize_heredoc</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">or</span> <span class="nx">match</span><span class="p">[</span><span class="mi">4</span><span class="p">],</span> <span class="p">{</span><span class="nx">quote</span><span class="p">}</span>
<span class="nx">@interpolate_string</span> <span class="s2">&quot;$quote$doc$quote&quot;</span>
<span class="vi">@line: </span><span class="o">+</span> <span class="nx">count</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s2">&quot;\n&quot;</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">length</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-14">#</a> </div> <p>Matches and conumes comments. We pass through comments into JavaScript,
so they're treated as real tokens, like any other part of the language.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">comment_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">match: </span><span class="nx">@chunk</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">COMMENT</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">match</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span>
<span class="nv">comment: </span><span class="nx">@sanitize_heredoc</span> <span class="nx">match</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span> <span class="p">{</span><span class="nv">herecomment: </span><span class="kc">true</span><span class="p">}</span>
<span class="nx">@token</span> <span class="s1">&#39;HERECOMMENT&#39;</span><span class="p">,</span> <span class="nx">comment</span><span class="p">.</span><span class="nx">split</span> <span class="nx">MULTILINER</span>
<span class="k">else</span>
<span class="nv">lines: </span><span class="nx">compact</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">replace</span><span class="p">(</span><span class="nx">COMMENT_CLEANER</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">).</span><span class="nx">split</span> <span class="nx">MULTILINER</span>
<span class="nv">i: </span><span class="nx">@tokens</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">if</span> <span class="nx">@unfinished</span><span class="p">()</span>
<span class="nv">i: </span><span class="o">-</span> <span class="mi">1</span> <span class="k">while</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">and</span> <span class="o">not</span> <span class="nx">include</span> <span class="nx">LINE_BREAK</span><span class="p">,</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;COMMENT&#39;</span><span class="p">,</span> <span class="nx">lines</span><span class="p">,</span> <span class="nx">@line</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;TERMINATOR&#39;</span><span class="p">,</span> <span class="s1">&#39;\n&#39;</span><span class="p">,</span> <span class="nx">@line</span><span class="p">])</span>
<span class="vi">@line: </span><span class="o">+</span> <span class="nx">count</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s2">&quot;\n&quot;</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">length</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-15"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-15">#</a> </div> <p>Matches JavaScript interpolated directly into the source via backticks.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">js_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nx">starts</span> <span class="nx">@chunk</span><span class="p">,</span> <span class="s1">&#39;`&#39;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">script: </span><span class="nx">@balanced_token</span> <span class="p">[</span><span class="s1">&#39;`&#39;</span><span class="p">,</span> <span class="s1">&#39;`&#39;</span><span class="p">]</span>
<span class="nx">@token</span> <span class="s1">&#39;JS&#39;</span><span class="p">,</span> <span class="nx">script</span><span class="p">.</span><span class="nx">replace</span> <span class="nx">JS_CLEANER</span><span class="p">,</span> <span class="s1">&#39;&#39;</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">script</span><span class="p">.</span><span class="nx">length</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-16"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-16">#</a> </div> <p>Matches regular expression literals. Lexing regular expressions is difficult
to distinguish from division, so we borrow some basic heuristics from
JavaScript and Ruby, borrow slash balancing from <code>@balanced_token</code>, and
borrow interpolation from <code>@interpolate_string</code>.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">regex_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nx">@chunk</span><span class="p">.</span><span class="nx">match</span> <span class="nx">REGEX_START</span>
<span class="k">return</span> <span class="kc">false</span> <span class="k">if</span> <span class="nx">include</span> <span class="nx">NOT_REGEX</span><span class="p">,</span> <span class="nx">@tag</span><span class="p">()</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">regex: </span><span class="nx">@balanced_token</span> <span class="p">[</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="s1">&#39;/&#39;</span><span class="p">]</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">end: </span><span class="nx">@chunk</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="nx">regex</span><span class="p">.</span><span class="nx">length</span><span class="p">).</span><span class="nx">match</span> <span class="nx">REGEX_END</span>
<span class="nv">regex: </span><span class="o">+</span> <span class="nv">flags: </span><span class="nx">end</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="k">if</span> <span class="nx">end</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="k">if</span> <span class="nx">regex</span><span class="p">.</span><span class="nx">match</span> <span class="nx">REGEX_INTERPOLATION</span>
<span class="nv">str: </span><span class="nx">regex</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="nv">str: </span><span class="nx">str</span><span class="p">.</span><span class="nx">replace</span> <span class="nx">REGEX_ESCAPE</span><span class="p">,</span> <span class="p">(</span><span class="nx">escaped</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s1">&#39;\\&#39;</span> <span class="o">+</span> <span class="nx">escaped</span>
<span class="vi">@tokens: </span><span class="nx">@tokens</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[[</span><span class="s1">&#39;(&#39;</span><span class="p">,</span> <span class="s1">&#39;(&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;NEW&#39;</span><span class="p">,</span> <span class="s1">&#39;new&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;IDENTIFIER&#39;</span><span class="p">,</span> <span class="s1">&#39;RegExp&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;CALL_START&#39;</span><span class="p">,</span> <span class="s1">&#39;(&#39;</span><span class="p">]]</span>
<span class="nx">@interpolate_string</span> <span class="s2">&quot;\&quot;$str\&quot;&quot;</span><span class="p">,</span> <span class="kc">yes</span>
<span class="vi">@tokens: </span><span class="nx">@tokens</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[[</span><span class="s1">&#39;,&#39;</span><span class="p">,</span> <span class="s1">&#39;,&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="s2">&quot;\&quot;$flags\&quot;&quot;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;)&#39;</span><span class="p">,</span> <span class="s1">&#39;)&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;)&#39;</span><span class="p">,</span> <span class="s1">&#39;)&#39;</span><span class="p">]]</span>
<span class="k">else</span>
<span class="nx">@token</span> <span class="s1">&#39;REGEX&#39;</span><span class="p">,</span> <span class="nx">regex</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">regex</span><span class="p">.</span><span class="nx">length</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-17"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-17">#</a> </div> <p>Matches a token in which which the passed delimiter pairs must be correctly
balanced (ie. strings, JS literals).</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">balanced_token: </span><span class="p">(</span><span class="nx">delimited</span><span class="p">...)</span> <span class="o">-&gt;</span>
<span class="nx">balanced_string</span> <span class="nx">@chunk</span><span class="p">,</span> <span class="nx">delimited</span></pre></div> </td> </tr> <tr id="section-18"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-18">#</a> </div> <p>Matches newlines, indents, and outdents, and determines which is which.
If we can detect that the current line is continued onto the the next line,
then the newline is suppressed:</p>
<pre><code>elements
.each( ... )
.map( ... )
</code></pre>
<p>Keeps track of the level of indentation, because a single outdent token
can close multiple indents, so we need to know how far in we happen to be.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">line_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">indent: </span><span class="nx">@match</span> <span class="nx">MULTI_DENT</span><span class="p">,</span> <span class="mi">1</span>
<span class="vi">@line: </span><span class="o">+</span> <span class="nx">count</span> <span class="nx">indent</span><span class="p">,</span> <span class="s2">&quot;\n&quot;</span>
<span class="vi">@i : </span><span class="o">+</span> <span class="nx">indent</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">prev: </span><span class="nx">@prev</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="nv">size: </span><span class="nx">indent</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">LAST_DENTS</span><span class="p">).</span><span class="nx">reverse</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">match</span><span class="p">(</span><span class="nx">LAST_DENT</span><span class="p">)[</span><span class="mi">1</span><span class="p">].</span><span class="nx">length</span>
<span class="nv">next_character: </span><span class="nx">@chunk</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">MULTI_DENT</span><span class="p">)[</span><span class="mi">4</span><span class="p">]</span>
<span class="nv">no_newlines: </span><span class="nx">next_character</span> <span class="o">is</span> <span class="s1">&#39;.&#39;</span> <span class="o">or</span> <span class="nx">@unfinished</span><span class="p">()</span>
<span class="k">if</span> <span class="nx">size</span> <span class="o">is</span> <span class="nx">@indent</span>
<span class="k">return</span> <span class="nx">@suppress_newlines</span><span class="p">()</span> <span class="k">if</span> <span class="nx">no_newlines</span>
<span class="k">return</span> <span class="nx">@newline_token</span> <span class="nx">indent</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">size</span> <span class="o">&gt;</span> <span class="nx">@indent</span>
<span class="k">return</span> <span class="nx">@suppress_newlines</span><span class="p">()</span> <span class="k">if</span> <span class="nx">no_newlines</span>
<span class="nv">diff: </span><span class="nx">size</span> <span class="o">-</span> <span class="nx">@indent</span>
<span class="nx">@token</span> <span class="s1">&#39;INDENT&#39;</span><span class="p">,</span> <span class="nx">diff</span>
<span class="nx">@indents</span><span class="p">.</span><span class="nx">push</span> <span class="nx">diff</span>
<span class="k">else</span>
<span class="nx">@outdent_token</span> <span class="nx">@indent</span> <span class="o">-</span> <span class="nx">size</span><span class="p">,</span> <span class="nx">no_newlines</span>
<span class="vi">@indent: </span><span class="nx">size</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-19"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-19">#</a> </div> <p>Record an outdent token or multiple tokens, if we happen to be moving back
inwards past several recorded indents.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">outdent_token: </span><span class="p">(</span><span class="nx">move_out</span><span class="p">,</span> <span class="nx">no_newlines</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">while</span> <span class="nx">move_out</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">and</span> <span class="nx">@indents</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">last_indent: </span><span class="nx">@indents</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="nx">@token</span> <span class="s1">&#39;OUTDENT&#39;</span><span class="p">,</span> <span class="nx">last_indent</span>
<span class="nv">move_out: </span><span class="o">-</span> <span class="nx">last_indent</span>
<span class="nx">@token</span> <span class="s1">&#39;TERMINATOR&#39;</span><span class="p">,</span> <span class="s2">&quot;\n&quot;</span> <span class="nx">unless</span> <span class="nx">@tag</span><span class="p">()</span> <span class="o">is</span> <span class="s1">&#39;TERMINATOR&#39;</span> <span class="o">or</span> <span class="nx">no_newlines</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-20"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-20">#</a> </div> <p>Matches and consumes non-meaningful whitespace. Tag the previous token
as being "spaced", because there are some cases where it makes a difference.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">whitespace_token: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">space: </span><span class="nx">@match</span> <span class="nx">WHITESPACE</span><span class="p">,</span> <span class="mi">1</span>
<span class="nv">prev: </span><span class="nx">@prev</span><span class="p">()</span>
<span class="nv">prev.spaced: </span><span class="kc">true</span> <span class="k">if</span> <span class="nx">prev</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">space</span><span class="p">.</span><span class="nx">length</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-21"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-21">#</a> </div> <p>Generate a newline token. Consecutive newlines get merged together.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">newline_token: </span><span class="p">(</span><span class="nx">newlines</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">@token</span> <span class="s1">&#39;TERMINATOR&#39;</span><span class="p">,</span> <span class="s2">&quot;\n&quot;</span> <span class="nx">unless</span> <span class="nx">@tag</span><span class="p">()</span> <span class="o">is</span> <span class="s1">&#39;TERMINATOR&#39;</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-22"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-22">#</a> </div> <p>Use a <code>\</code> at a line-ending to suppress the newline.
The slash is removed here once its job is done.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">suppress_newlines: </span><span class="o">-&gt;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span> <span class="k">if</span> <span class="nx">@value</span><span class="p">()</span> <span class="o">is</span> <span class="s2">&quot;\\&quot;</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-23"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-23">#</a> </div> <p>We treat all other single characters as a token. Eg.: <code>( ) , . !</code>
Multi-character operators are also literal tokens, so that Jison can assign
the proper order of operations. There are some symbols that we tag specially
here. <code>;</code> and newlines are both treated as a <code>TERMINATOR</code>, we distinguish
parentheses that indicate a method call from regular parentheses, and so on.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">literal_token: </span><span class="o">-&gt;</span>
<span class="nv">match: </span><span class="nx">@chunk</span><span class="p">.</span><span class="nx">match</span> <span class="nx">OPERATOR</span>
<span class="nv">value: </span><span class="nx">match</span> <span class="o">and</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="nv">space: </span><span class="nx">match</span> <span class="o">and</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="nx">@tag_parameters</span><span class="p">()</span> <span class="k">if</span> <span class="nx">value</span> <span class="o">and</span> <span class="nx">value</span><span class="p">.</span><span class="nx">match</span> <span class="nx">CODE</span>
<span class="nv">value: </span><span class="o">or</span> <span class="nx">@chunk</span><span class="p">.</span><span class="nx">substr</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span>
<span class="nv">prev_spaced: </span><span class="nx">@prev</span><span class="p">()</span> <span class="o">and</span> <span class="nx">@prev</span><span class="p">().</span><span class="nx">spaced</span>
<span class="nv">tag: </span><span class="nx">value</span>
<span class="k">if</span> <span class="nx">value</span><span class="p">.</span><span class="nx">match</span> <span class="nx">ASSIGNMENT</span>
<span class="nv">tag: </span><span class="s1">&#39;ASSIGN&#39;</span>
<span class="nx">@assignment_error</span><span class="p">()</span> <span class="k">if</span> <span class="nx">include</span> <span class="nx">JS_FORBIDDEN</span><span class="p">,</span> <span class="nx">@value</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">value</span> <span class="o">is</span> <span class="s1">&#39;;&#39;</span>
<span class="nv">tag: </span><span class="s1">&#39;TERMINATOR&#39;</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">value</span> <span class="o">is</span> <span class="s1">&#39;[&#39;</span> <span class="o">and</span> <span class="nx">@tag</span><span class="p">()</span> <span class="o">is</span> <span class="s1">&#39;?&#39;</span> <span class="o">and</span> <span class="o">not</span> <span class="nx">prev_spaced</span>
<span class="nv">tag: </span><span class="s1">&#39;SOAKED_INDEX_START&#39;</span>
<span class="vi">@soaked_index: </span><span class="kc">true</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">value</span> <span class="o">is</span> <span class="s1">&#39;]&#39;</span> <span class="o">and</span> <span class="nx">@soaked_index</span>
<span class="nv">tag: </span><span class="s1">&#39;SOAKED_INDEX_END&#39;</span>
<span class="vi">@soaked_index: </span><span class="kc">false</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">include</span><span class="p">(</span><span class="nx">CALLABLE</span><span class="p">,</span> <span class="nx">@tag</span><span class="p">())</span> <span class="o">and</span> <span class="o">not</span> <span class="nx">prev_spaced</span>
<span class="nv">tag: </span><span class="s1">&#39;CALL_START&#39;</span> <span class="k">if</span> <span class="nx">value</span> <span class="o">is</span> <span class="s1">&#39;(&#39;</span>
<span class="nv">tag: </span><span class="s1">&#39;INDEX_START&#39;</span> <span class="k">if</span> <span class="nx">value</span> <span class="o">is</span> <span class="s1">&#39;[&#39;</span>
<span class="vi">@i: </span><span class="o">+</span> <span class="nx">value</span><span class="p">.</span><span class="nx">length</span>
<span class="k">return</span> <span class="nx">@tag_half_assignment</span> <span class="nx">tag</span> <span class="k">if</span> <span class="nx">space</span> <span class="o">and</span> <span class="nx">prev_spaced</span> <span class="o">and</span> <span class="nx">@prev</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;ASSIGN&#39;</span> <span class="o">and</span> <span class="nx">include</span> <span class="nx">HALF_ASSIGNMENTS</span><span class="p">,</span> <span class="nx">tag</span>
<span class="nx">@token</span> <span class="nx">tag</span><span class="p">,</span> <span class="nx">value</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-24"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-24">#</a> </div> <h2>Token Manipulators</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-25"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-25">#</a> </div> <p>As we consume a new <code>IDENTIFIER</code>, look at the previous token to determine
if it's a special kind of accessor.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">name_access_type: </span><span class="o">-&gt;</span>
<span class="nx">@tag</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;PROTOTYPE_ACCESS&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="nx">@value</span><span class="p">()</span> <span class="o">is</span> <span class="s1">&#39;::&#39;</span>
<span class="k">if</span> <span class="nx">@value</span><span class="p">()</span> <span class="o">is</span> <span class="s1">&#39;.&#39;</span> <span class="o">and</span> <span class="o">not</span> <span class="p">(</span><span class="nx">@value</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">is</span> <span class="s1">&#39;.&#39;</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">@tag</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">is</span> <span class="s1">&#39;?&#39;</span>
<span class="nx">@tag</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;SOAK_ACCESS&#39;</span><span class="p">)</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">else</span>
<span class="nx">@tag</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;PROPERTY_ACCESS&#39;</span></pre></div> </td> </tr> <tr id="section-26"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-26">#</a> </div> <p>Sanitize a heredoc or herecomment by escaping internal double quotes and
erasing all external indentation on the left-hand side.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">sanitize_heredoc: </span><span class="p">(</span><span class="nx">doc</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">while</span> <span class="nv">match: </span><span class="nx">HEREDOC_INDENT</span><span class="p">.</span><span class="nx">exec</span> <span class="nx">doc</span>
<span class="nv">attempt: </span><span class="k">if</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">?</span> <span class="k">then</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="k">else</span> <span class="nx">match</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span>
<span class="nv">indent: </span><span class="nx">attempt</span> <span class="k">if</span> <span class="o">not</span> <span class="nx">indent</span> <span class="o">or</span> <span class="nx">attempt</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;</span> <span class="nx">indent</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">doc: </span><span class="nx">doc</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="s2">&quot;^&quot;</span> <span class="o">+</span><span class="nx">indent</span><span class="p">,</span> <span class="s1">&#39;gm&#39;</span><span class="p">),</span> <span class="s1">&#39;&#39;</span><span class="p">)</span>
<span class="k">return</span> <span class="nx">doc</span> <span class="k">if</span> <span class="nx">options</span><span class="p">.</span><span class="nx">herecomment</span>
<span class="nx">doc</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">MULTILINER</span><span class="p">,</span> <span class="s2">&quot;\\n&quot;</span><span class="p">)</span>
<span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">quote</span><span class="p">,</span> <span class="s1">&#39;g&#39;</span><span class="p">),</span> <span class="s1">&#39;\\&quot;&#39;</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-27"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-27">#</a> </div> <p>Tag a half assignment.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">tag_half_assignment: </span><span class="p">(</span><span class="nx">tag</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">last: </span><span class="nx">@tokens</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="s2">&quot;$tag=&quot;</span><span class="p">,</span> <span class="s2">&quot;$tag=&quot;</span><span class="p">,</span> <span class="nx">last</span><span class="p">[</span><span class="mi">2</span><span class="p">]]</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-28"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-28">#</a> </div> <p>A source of ambiguity in our grammar used to be parameter lists in function
definitions versus argument lists in function calls. Walk backwards, tagging
parameters specially in order to make things easier for the parser.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">tag_parameters: </span><span class="o">-&gt;</span>
<span class="k">return</span> <span class="k">if</span> <span class="nx">@tag</span><span class="p">()</span> <span class="o">isnt</span> <span class="s1">&#39;)&#39;</span>
<span class="nv">i: </span><span class="mi">0</span>
<span class="k">while</span> <span class="kc">true</span>
<span class="nv">i: </span><span class="o">+</span> <span class="mi">1</span>
<span class="nv">tok: </span><span class="nx">@prev</span> <span class="nx">i</span>
<span class="k">return</span> <span class="k">if</span> <span class="o">not</span> <span class="nx">tok</span>
<span class="k">switch</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">when</span> <span class="s1">&#39;IDENTIFIER&#39;</span> <span class="k">then</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;PARAM&#39;</span>
<span class="k">when</span> <span class="s1">&#39;)&#39;</span> <span class="k">then</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;PARAM_END&#39;</span>
<span class="k">when</span> <span class="s1">&#39;(&#39;</span><span class="p">,</span> <span class="s1">&#39;CALL_START&#39;</span> <span class="k">then</span> <span class="k">return</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;PARAM_START&#39;</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-29"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-29">#</a> </div> <p>Close up all remaining open blocks at the end of the file.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">close_indentation: </span><span class="o">-&gt;</span>
<span class="nx">@outdent_token</span> <span class="nx">@indent</span></pre></div> </td> </tr> <tr id="section-30"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-30">#</a> </div> <p>The error for when you try to use a forbidden word in JavaScript as
an identifier.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">identifier_error: </span><span class="p">(</span><span class="nx">word</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span> <span class="s2">&quot;SyntaxError: Reserved word \&quot;$word\&quot; on line ${@line + 1}&quot;</span></pre></div> </td> </tr> <tr id="section-31"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-31">#</a> </div> <p>The error for when you try to assign to a reserved word in JavaScript,
like "function" or "default".</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">assignment_error: </span><span class="o">-&gt;</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span> <span class="s2">&quot;SyntaxError: Reserved word \&quot;${@value()}\&quot; on line ${@line + 1} can&#39;t be assigned&quot;</span></pre></div> </td> </tr> <tr id="section-32"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-32">#</a> </div> <p>Expand variables and expressions inside double-quoted strings using
<a href="http://wiki.ecmascript.org/doku.php?id=strawman:string_interpolation">ECMA Harmony's interpolation syntax</a>
for substitution of bare variables as well as arbitrary expressions.</p>
<pre><code>"Hello $name."
"Hello ${name.capitalize()}."
</code></pre>
<p>If it encounters an interpolation, this method will recursively create a
new Lexer, tokenize the interpolated contents, and merge them into the
token stream.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">interpolate_string: </span><span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="nx">escape_quotes</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">if</span> <span class="nx">str</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;</span> <span class="mi">3</span> <span class="o">or</span> <span class="o">not</span> <span class="nx">starts</span> <span class="nx">str</span><span class="p">,</span> <span class="s1">&#39;&quot;&#39;</span>
<span class="nx">@token</span> <span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="nx">str</span>
<span class="k">else</span>
<span class="nv">lexer: </span> <span class="k">new</span> <span class="nx">Lexer</span><span class="p">()</span>
<span class="nv">tokens: </span> <span class="p">[]</span>
<span class="nv">quote: </span> <span class="nx">str</span><span class="p">.</span><span class="nx">substring</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span>
<span class="p">[</span><span class="nx">i</span><span class="p">,</span> <span class="nx">pi</span><span class="p">]</span><span class="o">:</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="k">while</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">str</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">if</span> <span class="nx">starts</span> <span class="nx">str</span><span class="p">,</span> <span class="s1">&#39;\\&#39;</span><span class="p">,</span> <span class="nx">i</span>
<span class="nv">i: </span><span class="o">+</span> <span class="mi">1</span>
<span class="k">else</span> <span class="k">if</span> <span class="nv">match: </span><span class="nx">str</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="nx">i</span><span class="p">).</span><span class="nx">match</span> <span class="nx">INTERPOLATION</span>
<span class="p">[</span><span class="nx">group</span><span class="p">,</span> <span class="nx">interp</span><span class="p">]</span><span class="o">:</span> <span class="nx">match</span>
<span class="nv">interp: </span><span class="s2">&quot;this.${ interp.substring(1) }&quot;</span> <span class="k">if</span> <span class="nx">starts</span> <span class="nx">interp</span><span class="p">,</span> <span class="s1">&#39;@&#39;</span>
<span class="nx">tokens</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="s2">&quot;$quote${ str.substring(pi, i) }$quote&quot;</span><span class="p">]</span> <span class="k">if</span> <span class="nx">pi</span> <span class="o">&lt;</span> <span class="nx">i</span>
<span class="nx">tokens</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="s1">&#39;IDENTIFIER&#39;</span><span class="p">,</span> <span class="nx">interp</span><span class="p">]</span>
<span class="nv">i: </span><span class="o">+</span> <span class="nx">group</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span>
<span class="nv">pi: </span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">expr: </span><span class="nx">balanced_string</span> <span class="nx">str</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="nx">i</span><span class="p">),</span> <span class="p">[[</span><span class="s1">&#39;${&#39;</span><span class="p">,</span> <span class="s1">&#39;}&#39;</span><span class="p">]])</span>
<span class="nx">tokens</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="s2">&quot;$quote${ str.substring(pi, i) }$quote&quot;</span><span class="p">]</span> <span class="k">if</span> <span class="nx">pi</span> <span class="o">&lt;</span> <span class="nx">i</span>
<span class="nv">inner: </span><span class="nx">expr</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nx">expr</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">inner</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">nested: </span><span class="nx">lexer</span><span class="p">.</span><span class="nx">tokenize</span> <span class="s2">&quot;($inner)&quot;</span><span class="p">,</span> <span class="p">{</span><span class="nv">line: </span><span class="nx">@line</span><span class="p">}</span>
<span class="p">(</span><span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;)&#39;</span><span class="p">)</span> <span class="k">for</span> <span class="nx">tok</span><span class="p">,</span> <span class="nx">idx</span> <span class="k">in</span> <span class="nx">nested</span> <span class="k">when</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;CALL_END&#39;</span>
<span class="nx">nested</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="nx">tokens</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="s1">&#39;TOKENS&#39;</span><span class="p">,</span> <span class="nx">nested</span><span class="p">]</span>
<span class="k">else</span>
<span class="nx">tokens</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="s2">&quot;$quote$quote&quot;</span><span class="p">]</span>
<span class="nv">i: </span><span class="o">+</span> <span class="nx">expr</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span>
<span class="nv">pi: </span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="nv">i: </span><span class="o">+</span> <span class="mi">1</span>
<span class="nx">tokens</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="s2">&quot;$quote${ str.substring(pi, i) }$quote&quot;</span><span class="p">]</span> <span class="k">if</span> <span class="nx">pi</span> <span class="o">&lt;</span> <span class="nx">i</span> <span class="o">and</span> <span class="nx">pi</span> <span class="o">&lt;</span> <span class="nx">str</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span>
<span class="nx">tokens</span><span class="p">.</span><span class="nx">unshift</span> <span class="p">[</span><span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="s1">&#39;&quot;&quot;&#39;</span><span class="p">]</span> <span class="nx">unless</span> <span class="nx">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;STRING&#39;</span>
<span class="nv">interpolated: </span><span class="nx">tokens</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">1</span>
<span class="nx">@token</span> <span class="s1">&#39;(&#39;</span><span class="p">,</span> <span class="s1">&#39;(&#39;</span> <span class="k">if</span> <span class="nx">interpolated</span>
<span class="k">for</span> <span class="nx">token</span><span class="p">,</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">tokens</span>
<span class="p">[</span><span class="nx">tag</span><span class="p">,</span> <span class="nx">value</span><span class="p">]</span><span class="o">:</span> <span class="nx">token</span>
<span class="k">if</span> <span class="nx">tag</span> <span class="o">is</span> <span class="s1">&#39;TOKENS&#39;</span>
<span class="vi">@tokens: </span><span class="nx">@tokens</span><span class="p">.</span><span class="nx">concat</span> <span class="nx">value</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">tag</span> <span class="o">is</span> <span class="s1">&#39;STRING&#39;</span> <span class="o">and</span> <span class="nx">escape_quotes</span>
<span class="nv">escaped: </span><span class="nx">value</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nx">value</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/&quot;/g</span><span class="p">,</span> <span class="s1">&#39;\\&quot;&#39;</span><span class="p">)</span>
<span class="nx">@token</span> <span class="nx">tag</span><span class="p">,</span> <span class="s2">&quot;\&quot;$escaped\&quot;&quot;</span>
<span class="k">else</span>
<span class="nx">@token</span> <span class="nx">tag</span><span class="p">,</span> <span class="nx">value</span>
<span class="nx">@token</span> <span class="s1">&#39;+&#39;</span><span class="p">,</span> <span class="s1">&#39;+&#39;</span> <span class="k">if</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">tokens</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span>
<span class="nx">@token</span> <span class="s1">&#39;)&#39;</span><span class="p">,</span> <span class="s1">&#39;)&#39;</span> <span class="k">if</span> <span class="nx">interpolated</span>
<span class="nx">tokens</span></pre></div> </td> </tr> <tr id="section-33"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-33">#</a> </div> <h2>Helpers</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-34"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-34">#</a> </div> <p>Add a token to the results, taking note of the line number.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">token: </span><span class="p">(</span><span class="nx">tag</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="nx">tag</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">@line</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-35"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-35">#</a> </div> <p>Peek at a tag in the current token stream.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">tag: </span><span class="p">(</span><span class="nx">index</span><span class="p">,</span> <span class="nx">new_tag</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">return</span> <span class="nx">unless</span> <span class="nv">tok: </span><span class="nx">@prev</span> <span class="nx">index</span>
<span class="k">return</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">:</span> <span class="nx">new_tag</span> <span class="k">if</span> <span class="nx">new_tag</span><span class="o">?</span>
<span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-36"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-36">#</a> </div> <p>Peek at a value in the current token stream.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">value: </span><span class="p">(</span><span class="nx">index</span><span class="p">,</span> <span class="nx">val</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">return</span> <span class="nx">unless</span> <span class="nv">tok: </span><span class="nx">@prev</span> <span class="nx">index</span>
<span class="k">return</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">:</span> <span class="nx">val</span> <span class="k">if</span> <span class="nx">val</span><span class="o">?</span>
<span class="nx">tok</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-37"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-37">#</a> </div> <p>Peek at a previous token, entire.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">prev: </span><span class="p">(</span><span class="nx">index</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">@tokens</span><span class="p">[</span><span class="nx">@tokens</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="p">(</span><span class="nx">index</span> <span class="o">or</span> <span class="mi">1</span><span class="p">)]</span></pre></div> </td> </tr> <tr id="section-38"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-38">#</a> </div> <p>Attempt to match a string against the current chunk, returning the indexed
match if successful, and <code>false</code> otherwise.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">match: </span><span class="p">(</span><span class="nx">regex</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">false</span> <span class="nx">unless</span> <span class="nv">m: </span><span class="nx">@chunk</span><span class="p">.</span><span class="nx">match</span> <span class="nx">regex</span>
<span class="k">if</span> <span class="nx">m</span> <span class="k">then</span> <span class="nx">m</span><span class="p">[</span><span class="nx">index</span><span class="p">]</span> <span class="k">else</span> <span class="kc">false</span></pre></div> </td> </tr> <tr id="section-39"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-39">#</a> </div> <p>Are we in the midst of an unfinished expression?</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">unfinished: </span><span class="o">-&gt;</span>
<span class="nv">prev: </span><span class="nx">@prev</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="nx">@value</span><span class="p">()</span> <span class="o">and</span> <span class="nx">@value</span><span class="p">().</span><span class="nx">match</span> <span class="o">and</span> <span class="nx">@value</span><span class="p">().</span><span class="nx">match</span><span class="p">(</span><span class="nx">NO_NEWLINE</span><span class="p">)</span> <span class="o">and</span>
<span class="nx">prev</span> <span class="o">and</span> <span class="p">(</span><span class="nx">prev</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">isnt</span> <span class="s1">&#39;.&#39;</span><span class="p">)</span> <span class="o">and</span> <span class="o">not</span> <span class="nx">@value</span><span class="p">().</span><span class="nx">match</span><span class="p">(</span><span class="nx">CODE</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-40"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-40">#</a> </div> <h2>Lexer Properties</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-41"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-41">#</a> </div> <p>There are no exensions to the core lexer by default.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="vi">@extensions: </span><span class="p">[]</span></pre></div> </td> </tr> <tr id="section-42"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-42">#</a> </div> <h2>Constants</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-43"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-43">#</a> </div> <p>Keywords that CoffeeScript shares in common with JavaScript.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">JS_KEYWORDS: </span><span class="p">[</span>
<span class="s2">&quot;if&quot;</span><span class="p">,</span> <span class="s2">&quot;else&quot;</span><span class="p">,</span>
<span class="s2">&quot;true&quot;</span><span class="p">,</span> <span class="s2">&quot;false&quot;</span><span class="p">,</span>
<span class="s2">&quot;new&quot;</span><span class="p">,</span> <span class="s2">&quot;return&quot;</span><span class="p">,</span>
<span class="s2">&quot;try&quot;</span><span class="p">,</span> <span class="s2">&quot;catch&quot;</span><span class="p">,</span> <span class="s2">&quot;finally&quot;</span><span class="p">,</span> <span class="s2">&quot;throw&quot;</span><span class="p">,</span>
<span class="s2">&quot;break&quot;</span><span class="p">,</span> <span class="s2">&quot;continue&quot;</span><span class="p">,</span>
<span class="s2">&quot;for&quot;</span><span class="p">,</span> <span class="s2">&quot;in&quot;</span><span class="p">,</span> <span class="s2">&quot;while&quot;</span><span class="p">,</span>
<span class="s2">&quot;delete&quot;</span><span class="p">,</span> <span class="s2">&quot;instanceof&quot;</span><span class="p">,</span> <span class="s2">&quot;typeof&quot;</span><span class="p">,</span>
<span class="s2">&quot;switch&quot;</span><span class="p">,</span> <span class="s2">&quot;super&quot;</span><span class="p">,</span> <span class="s2">&quot;extends&quot;</span><span class="p">,</span> <span class="s2">&quot;class&quot;</span><span class="p">,</span>
<span class="s2">&quot;this&quot;</span><span class="p">,</span> <span class="s2">&quot;null&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-44"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-44">#</a> </div> <p>CoffeeScript-only keywords, which we're more relaxed about allowing. They can't
be used standalone, but you can reference them as an attached property.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">COFFEE_ALIASES: </span> <span class="p">[</span><span class="s2">&quot;and&quot;</span><span class="p">,</span> <span class="s2">&quot;or&quot;</span><span class="p">,</span> <span class="s2">&quot;is&quot;</span><span class="p">,</span> <span class="s2">&quot;isnt&quot;</span><span class="p">,</span> <span class="s2">&quot;not&quot;</span><span class="p">]</span>
<span class="nv">COFFEE_KEYWORDS: </span><span class="nx">COFFEE_ALIASES</span><span class="p">.</span><span class="nx">concat</span> <span class="p">[</span>
<span class="s2">&quot;then&quot;</span><span class="p">,</span> <span class="s2">&quot;unless&quot;</span><span class="p">,</span> <span class="s2">&quot;until&quot;</span><span class="p">,</span>
<span class="s2">&quot;yes&quot;</span><span class="p">,</span> <span class="s2">&quot;no&quot;</span><span class="p">,</span> <span class="s2">&quot;on&quot;</span><span class="p">,</span> <span class="s2">&quot;off&quot;</span><span class="p">,</span>
<span class="s2">&quot;of&quot;</span><span class="p">,</span> <span class="s2">&quot;by&quot;</span><span class="p">,</span> <span class="s2">&quot;where&quot;</span><span class="p">,</span> <span class="s2">&quot;when&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-45"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-45">#</a> </div> <p>The combined list of keywords is the superset that gets passed verbatim to
the parser.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">KEYWORDS: </span><span class="nx">JS_KEYWORDS</span><span class="p">.</span><span class="nx">concat</span> <span class="nx">COFFEE_KEYWORDS</span></pre></div> </td> </tr> <tr id="section-46"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-46">#</a> </div> <p>The list of keywords that are reserved by JavaScript, but not used, or are
used by CoffeeScript internally. We throw an error when these are encountered,
to avoid having a JavaScript error at runtime.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">RESERVED: </span><span class="p">[</span>
<span class="s2">&quot;case&quot;</span><span class="p">,</span> <span class="s2">&quot;default&quot;</span><span class="p">,</span> <span class="s2">&quot;do&quot;</span><span class="p">,</span> <span class="s2">&quot;function&quot;</span><span class="p">,</span> <span class="s2">&quot;var&quot;</span><span class="p">,</span> <span class="s2">&quot;void&quot;</span><span class="p">,</span> <span class="s2">&quot;with&quot;</span>
<span class="s2">&quot;const&quot;</span><span class="p">,</span> <span class="s2">&quot;let&quot;</span><span class="p">,</span> <span class="s2">&quot;enum&quot;</span><span class="p">,</span> <span class="s2">&quot;export&quot;</span><span class="p">,</span> <span class="s2">&quot;import&quot;</span><span class="p">,</span> <span class="s2">&quot;native&quot;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-47"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-47">#</a> </div> <p>The superset of both JavaScript keywords and reserved words, none of which may
be used as identifiers or properties.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">JS_FORBIDDEN: </span><span class="nx">JS_KEYWORDS</span><span class="p">.</span><span class="nx">concat</span> <span class="nx">RESERVED</span></pre></div> </td> </tr> <tr id="section-48"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-48">#</a> </div> <p>Token matching regexes.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">IDENTIFIER : </span><span class="sr">/^([a-zA-Z\$_](\w|\$)*)/</span>
<span class="nv">NUMBER : </span><span class="sr">/^(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i</span>
<span class="nv">HEREDOC : </span><span class="sr">/^(&quot;{6}|&#39;{6}|&quot;{3}\n?([\s\S]*?)\n?([ \t]*)&quot;{3}|&#39;{3}\n?([\s\S]*?)\n?([ \t]*)&#39;{3})/</span>
<span class="nv">INTERPOLATION : </span><span class="sr">/^\$([a-zA-Z_@]\w*(\.\w+)*)/</span>
<span class="nv">OPERATOR : </span><span class="sr">/^([+\*&amp;|\/\-%=&lt;&gt;:!?]+)([ \t]*)/</span>
<span class="nv">WHITESPACE : </span><span class="sr">/^([ \t]+)/</span>
<span class="nv">COMMENT : </span><span class="sr">/^((\n?[ \t]*)?#{3}(?!#)\n*([\s\S]*?)\n*([ \t]*)#{3}|((\n?[ \t]*)?#[^\n]*)+)/</span>
<span class="nv">CODE : </span><span class="sr">/^((-|=)&gt;)/</span>
<span class="nv">MULTI_DENT : </span><span class="sr">/^((\n([ \t]*))+)(\.)?/</span>
<span class="nv">LAST_DENTS : </span><span class="sr">/\n([ \t]*)/g</span>
<span class="nv">LAST_DENT : </span><span class="sr">/\n([ \t]*)/</span>
<span class="nv">ASSIGNMENT : </span><span class="sr">/^(:|=)$/</span></pre></div> </td> </tr> <tr id="section-49"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-49">#</a> </div> <p>Regex-matching-regexes.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">REGEX_START : </span><span class="sr">/^\/[^\/ ]/</span>
<span class="nv">REGEX_INTERPOLATION: </span><span class="sr">/([^\\]\$[a-zA-Z_@]|[^\\]\$\{.*[^\\]\})/</span>
<span class="nv">REGEX_END : </span><span class="sr">/^(([imgy]{1,4})\b|\W)/</span>
<span class="nv">REGEX_ESCAPE : </span><span class="sr">/\\[^\$]/g</span></pre></div> </td> </tr> <tr id="section-50"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-50">#</a> </div> <p>Token cleaning regexes.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">JS_CLEANER : </span><span class="sr">/(^`|`$)/g</span>
<span class="nv">MULTILINER : </span><span class="sr">/\n/g</span>
<span class="nv">STRING_NEWLINES : </span><span class="sr">/\n[ \t]*/g</span>
<span class="nv">COMMENT_CLEANER : </span><span class="sr">/(^[ \t]*#|\n[ \t]*$)/mg</span>
<span class="nv">NO_NEWLINE : </span><span class="sr">/^([+\*&amp;|\/\-%=&lt;&gt;:!.\\][&lt;&gt;=&amp;|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/</span>
<span class="nv">HEREDOC_INDENT : </span><span class="sr">/(\n+([ \t]*)|^([ \t]+))/g</span></pre></div> </td> </tr> <tr id="section-51"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-51">#</a> </div> <p>Tokens which a regular expression will never immediately follow, but which
a division operator might.</p>
<p>See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions</p>
<p>Our list is shorter, due to sans-parentheses method calls.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">NOT_REGEX: </span><span class="p">[</span>
<span class="s1">&#39;NUMBER&#39;</span><span class="p">,</span> <span class="s1">&#39;REGEX&#39;</span><span class="p">,</span> <span class="s1">&#39;++&#39;</span><span class="p">,</span> <span class="s1">&#39;--&#39;</span><span class="p">,</span> <span class="s1">&#39;FALSE&#39;</span><span class="p">,</span> <span class="s1">&#39;NULL&#39;</span><span class="p">,</span> <span class="s1">&#39;TRUE&#39;</span><span class="p">,</span> <span class="s1">&#39;]&#39;</span>
<span class="p">]</span></pre></div> </td> </tr> <tr id="section-52"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-52">#</a> </div> <p>Tokens which could legitimately be invoked or indexed. A opening
parentheses or bracket following these tokens will be recorded as the start
of a function invocation or indexing operation.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">CALLABLE: </span><span class="p">[</span><span class="s1">&#39;IDENTIFIER&#39;</span><span class="p">,</span> <span class="s1">&#39;SUPER&#39;</span><span class="p">,</span> <span class="s1">&#39;)&#39;</span><span class="p">,</span> <span class="s1">&#39;]&#39;</span><span class="p">,</span> <span class="s1">&#39;}&#39;</span><span class="p">,</span> <span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="s1">&#39;@&#39;</span><span class="p">,</span> <span class="s1">&#39;THIS&#39;</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-53"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-53">#</a> </div> <p>Tokens that indicate an access -- keywords immediately following will be
treated as identifiers.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">ACCESSORS: </span><span class="p">[</span><span class="s1">&#39;PROPERTY_ACCESS&#39;</span><span class="p">,</span> <span class="s1">&#39;PROTOTYPE_ACCESS&#39;</span><span class="p">,</span> <span class="s1">&#39;SOAK_ACCESS&#39;</span><span class="p">,</span> <span class="s1">&#39;@&#39;</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-54"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-54">#</a> </div> <p>Tokens that, when immediately preceding a <code>WHEN</code>, indicate that the <code>WHEN</code>
occurs at the start of a line. We disambiguate these from trailing whens to
avoid an ambiguity in the grammar.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">LINE_BREAK: </span><span class="p">[</span><span class="s1">&#39;INDENT&#39;</span><span class="p">,</span> <span class="s1">&#39;OUTDENT&#39;</span><span class="p">,</span> <span class="s1">&#39;TERMINATOR&#39;</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-55"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-55">#</a> </div> <p>Half-assignments...</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">HALF_ASSIGNMENTS: </span><span class="p">[</span><span class="s1">&#39;-&#39;</span><span class="p">,</span> <span class="s1">&#39;+&#39;</span><span class="p">,</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="s1">&#39;*&#39;</span><span class="p">,</span> <span class="s1">&#39;%&#39;</span><span class="p">,</span> <span class="s1">&#39;||&#39;</span><span class="p">,</span> <span class="s1">&#39;&amp;&amp;&#39;</span><span class="p">,</span> <span class="s1">&#39;?&#39;</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-56"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-56">#</a> </div> <p>Conversions from CoffeeScript operators into JavaScript ones.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">CONVERSIONS: </span><span class="p">{</span>
<span class="s1">&#39;and&#39;</span><span class="o">:</span> <span class="s1">&#39;&amp;&amp;&#39;</span>
<span class="s1">&#39;or&#39;</span><span class="o">:</span> <span class="s1">&#39;||&#39;</span>
<span class="s1">&#39;is&#39;</span><span class="o">:</span> <span class="s1">&#39;==&#39;</span>
<span class="s1">&#39;isnt&#39;</span><span class="o">:</span> <span class="s1">&#39;!=&#39;</span>
<span class="s1">&#39;not&#39;</span><span class="o">:</span> <span class="s1">&#39;!&#39;</span>
<span class="p">}</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
<!DOCTYPE html> <html> <head> <title>optparse.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> optparse.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>A simple <strong>OptionParser</strong> class to parse option flags from the command-line.
Use it like so:</p>
<pre><code>parser: new OptionParser switches, help_banner
options: parser.parse process.argv
</code></pre> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.OptionParser: </span><span class="nx">class</span> <span class="nx">OptionParser</span></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>Initialize with a list of valid options, in the form:</p>
<pre><code>[short-flag, long-flag, description]
</code></pre>
<p>Along with an an optional banner for the usage help.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">constructor: </span><span class="p">(</span><span class="nx">rules</span><span class="p">,</span> <span class="nx">banner</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="vi">@banner: </span> <span class="nx">banner</span>
<span class="vi">@rules: </span> <span class="nx">build_rules</span><span class="p">(</span><span class="nx">rules</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>Parse the list of arguments, populating an <code>options</code> object with all of the
specified options, and returning it. <code>options.arguments</code> will be an array
containing the remaning non-option arguments. This is a simpler API than
many option parsers that allow you to attach callback actions for every
flag. Instead, you're responsible for interpreting the options object.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">parse: </span><span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">options: </span><span class="p">{</span><span class="nv">arguments: </span><span class="p">[]}</span>
<span class="nv">args: </span><span class="nx">normalize_arguments</span> <span class="nx">args</span>
<span class="k">while</span> <span class="p">(</span><span class="nv">arg: </span><span class="nx">args</span><span class="p">.</span><span class="nx">shift</span><span class="p">())</span>
<span class="nv">is_option: </span><span class="o">!!</span><span class="p">(</span><span class="nx">arg</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">LONG_FLAG</span><span class="p">)</span> <span class="o">or</span> <span class="nx">arg</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">SHORT_FLAG</span><span class="p">))</span>
<span class="nv">matched_rule: </span><span class="kc">no</span>
<span class="k">for</span> <span class="nx">rule</span> <span class="k">in</span> <span class="nx">@rules</span>
<span class="k">if</span> <span class="nx">rule</span><span class="p">.</span><span class="nx">short_flag</span> <span class="o">is</span> <span class="nx">arg</span> <span class="o">or</span> <span class="nx">rule</span><span class="p">.</span><span class="nx">long_flag</span> <span class="o">is</span> <span class="nx">arg</span>
<span class="nx">options</span><span class="p">[</span><span class="nx">rule</span><span class="p">.</span><span class="nx">name</span><span class="p">]</span><span class="o">:</span> <span class="k">if</span> <span class="nx">rule</span><span class="p">.</span><span class="nx">has_argument</span> <span class="k">then</span> <span class="nx">args</span><span class="p">.</span><span class="nx">shift</span><span class="p">()</span> <span class="k">else</span> <span class="kc">true</span>
<span class="nv">matched_rule: </span><span class="kc">yes</span>
<span class="k">break</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span> <span class="s2">&quot;unrecognized option: $arg&quot;</span> <span class="k">if</span> <span class="nx">is_option</span> <span class="o">and</span> <span class="o">not</span> <span class="nx">matched_rule</span>
<span class="nx">options</span><span class="p">.</span><span class="nx">arguments</span><span class="p">.</span><span class="nx">push</span> <span class="nx">arg</span> <span class="nx">unless</span> <span class="nx">is_option</span>
<span class="nx">options</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>Return the help text for this <strong>OptionParser</strong>, listing and describing all
of the valid options, for <code>--help</code> and such.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">help: </span><span class="o">-&gt;</span>
<span class="nv">lines: </span><span class="p">[</span><span class="s1">&#39;Available options:&#39;</span><span class="p">]</span>
<span class="nx">lines</span><span class="p">.</span><span class="nx">unshift</span> <span class="s2">&quot;$@banner\n&quot;</span> <span class="k">if</span> <span class="nx">@banner</span>
<span class="k">for</span> <span class="nx">rule</span> <span class="k">in</span> <span class="nx">@rules</span>
<span class="nv">spaces: </span> <span class="mi">15</span> <span class="o">-</span> <span class="nx">rule</span><span class="p">.</span><span class="nx">long_flag</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">spaces: </span> <span class="k">if</span> <span class="nx">spaces</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">then</span> <span class="p">(</span><span class="s1">&#39; &#39;</span> <span class="k">for</span> <span class="nx">i</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">spaces</span><span class="p">]).</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="k">else</span> <span class="s1">&#39;&#39;</span>
<span class="nv">let_part: </span><span class="k">if</span> <span class="nx">rule</span><span class="p">.</span><span class="nx">short_flag</span> <span class="k">then</span> <span class="nx">rule</span><span class="p">.</span><span class="nx">short_flag</span> <span class="o">+</span> <span class="s1">&#39;, &#39;</span> <span class="k">else</span> <span class="s1">&#39; &#39;</span>
<span class="nx">lines</span><span class="p">.</span><span class="nx">push</span> <span class="s2">&quot; $let_part$rule.long_flag$spaces$rule.description&quot;</span>
<span class="s2">&quot;\n${ lines.join(&#39;\n&#39;) }\n&quot;</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <h2>Helpers</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p>Regex matchers for option flags.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">LONG_FLAG: </span> <span class="sr">/^(--\w[\w\-]+)/</span>
<span class="nv">SHORT_FLAG: </span><span class="sr">/^(-\w)/</span>
<span class="nv">MULTI_FLAG: </span><span class="sr">/^-(\w{2,})/</span>
<span class="nv">OPTIONAL: </span> <span class="sr">/\[(.+)\]/</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>Build and return the list of option rules. If the optional <em>short-flag</em> is
unspecified, leave it out by padding with <code>null</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">build_rules: </span><span class="p">(</span><span class="nx">rules</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">for</span> <span class="nx">tuple</span> <span class="k">in</span> <span class="nx">rules</span>
<span class="nx">tuple</span><span class="p">.</span><span class="nx">unshift</span> <span class="kc">null</span> <span class="k">if</span> <span class="nx">tuple</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;</span> <span class="mi">3</span>
<span class="nx">build_rule</span> <span class="nx">tuple</span><span class="p">...</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>Build a rule from a <code>-o</code> short flag, a <code>--output [DIR]</code> long flag, and the
description of what the option does.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">build_rule: </span><span class="p">(</span><span class="nx">short_flag</span><span class="p">,</span> <span class="nx">long_flag</span><span class="p">,</span> <span class="nx">description</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">match: </span> <span class="nx">long_flag</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">OPTIONAL</span><span class="p">)</span>
<span class="nv">long_flag: </span> <span class="nx">long_flag</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">LONG_FLAG</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
<span class="p">{</span>
<span class="nv">name: </span> <span class="nx">long_flag</span><span class="p">.</span><span class="nx">substr</span> <span class="mi">2</span>
<span class="nv">short_flag: </span> <span class="nx">short_flag</span>
<span class="nv">long_flag: </span> <span class="nx">long_flag</span>
<span class="nv">description: </span> <span class="nx">description</span>
<span class="nv">has_argument: </span><span class="o">!!</span><span class="p">(</span><span class="nx">match</span> <span class="o">and</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="p">}</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>Normalize arguments by expanding merged flags into multiple flags. This allows
you to have <code>-wl</code> be the same as <code>--watch --lint</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">normalize_arguments: </span><span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">args: </span><span class="nx">args</span><span class="p">.</span><span class="nx">slice</span> <span class="mi">0</span>
<span class="nv">result: </span><span class="p">[]</span>
<span class="k">for</span> <span class="nx">arg</span> <span class="k">in</span> <span class="nx">args</span>
<span class="k">if</span> <span class="nv">match: </span><span class="nx">arg</span><span class="p">.</span><span class="nx">match</span> <span class="nx">MULTI_FLAG</span>
<span class="nx">result</span><span class="p">.</span><span class="nx">push</span> <span class="s1">&#39;-&#39;</span> <span class="o">+</span> <span class="nx">l</span> <span class="k">for</span> <span class="nx">l</span> <span class="k">in</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">split</span> <span class="s1">&#39;&#39;</span>
<span class="k">else</span>
<span class="nx">result</span><span class="p">.</span><span class="nx">push</span> <span class="nx">arg</span>
<span class="nx">result</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html> <html> <head> <title>repl.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> repl.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>A very simple Read-Eval-Print-Loop. Compiles one line at a time to JavaScript
and evaluates it. Good for simple tests, or poking around the <strong>Node.js</strong> API.
Using it looks like this:</p>
<pre><code>coffee&gt; puts "$num bottles of beer" for num in [99..1]
</code></pre> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>Require the <strong>coffee-script</strong> module to get access to the compiler.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">CoffeeScript: </span><span class="nx">require</span> <span class="s1">&#39;./coffee-script&#39;</span>
<span class="nv">helpers: </span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./helpers&#39;</span><span class="p">).</span><span class="nx">helpers</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>Our prompt.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">prompt: </span><span class="s1">&#39;coffee&gt; &#39;</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>Quick alias for quitting the REPL.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">helpers</span><span class="p">.</span><span class="nx">extend</span> <span class="nx">global</span><span class="p">,</span> <span class="p">{</span>
<span class="nv">quit: </span><span class="o">-&gt;</span> <span class="nx">process</span><span class="p">.</span><span class="nx">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="p">}</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>The main REPL function. <strong>run</strong> is called every time a line of code is entered.
Attempt to evaluate the command. If there's an exception, print it out instead
of exiting.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">run: </span><span class="p">(</span><span class="nx">buffer</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">try</span>
<span class="nv">val: </span><span class="nx">CoffeeScript</span><span class="p">.</span><span class="nx">run</span> <span class="nx">buffer</span><span class="p">.</span><span class="nx">toString</span><span class="p">(),</span> <span class="p">{</span><span class="nv">no_wrap: </span><span class="kc">true</span><span class="p">,</span> <span class="nv">globals: </span><span class="kc">true</span><span class="p">,</span> <span class="nv">source: </span><span class="s1">&#39;repl&#39;</span><span class="p">}</span>
<span class="nx">p</span> <span class="nx">val</span> <span class="k">if</span> <span class="nx">val</span> <span class="o">isnt</span> <span class="kc">undefined</span>
<span class="k">catch</span> <span class="nx">err</span>
<span class="nx">puts</span> <span class="nx">err</span><span class="p">.</span><span class="nx">stack</span> <span class="o">or</span> <span class="nx">err</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span>
<span class="nx">print</span> <span class="nx">prompt</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p>Start up the REPL by opening <strong>stdin</strong> and listening for input.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">stdin: </span><span class="nx">process</span><span class="p">.</span><span class="nx">openStdin</span><span class="p">()</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">addListener</span> <span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="nx">run</span>
<span class="nx">print</span> <span class="nx">prompt</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,228 @@
<!DOCTYPE html> <html> <head> <title>rewriter.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> rewriter.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>The CoffeeScript language has a good deal of optional syntax, implicit syntax,
and shorthand syntax. This can greatly complicate a grammar and bloat
the resulting parse table. Instead of making the parser handle it all, we take
a series of passes over the token stream, using this <strong>Rewriter</strong> to convert
shorthand into the unambiguous long form, add implicit indentation and
parentheses, balance incorrect nestings, and generally clean things up.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>Set up exported variables for both Node.js and the browser.</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">if</span> <span class="nx">process</span><span class="o">?</span>
<span class="p">{</span><span class="nx">helpers</span><span class="p">}</span><span class="o">:</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./helpers&#39;</span><span class="p">)</span>
<span class="k">else</span>
<span class="k">this</span><span class="p">.</span><span class="nv">exports: </span><span class="k">this</span>
<span class="nv">helpers: </span> <span class="k">this</span><span class="p">.</span><span class="nx">helpers</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>Import the helpers we need.</p> </td> <td class="code"> <div class="highlight"><pre><span class="p">{</span><span class="nx">include</span><span class="p">}</span><span class="o">:</span> <span class="nx">helpers</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>The <strong>Rewriter</strong> class is used by the <a href="lexer.html">Lexer</a>, directly against
its internal array of tokens.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.Rewriter: </span><span class="nx">class</span> <span class="nx">Rewriter</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>Rewrite the token stream in multiple passes, one logical filter at
a time. This could certainly be changed into a single pass through the
stream, with a big ol' efficient switch, but it's much nicer to work with
like this. The order of these passes matters -- indentation must be
corrected before implicit parentheses can be wrapped around blocks of code.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">rewrite: </span><span class="p">(</span><span class="nx">tokens</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="vi">@tokens: </span><span class="nx">tokens</span>
<span class="nx">@adjust_comments</span><span class="p">()</span>
<span class="nx">@remove_leading_newlines</span><span class="p">()</span>
<span class="nx">@remove_mid_expression_newlines</span><span class="p">()</span>
<span class="nx">@close_open_calls_and_indexes</span><span class="p">()</span>
<span class="nx">@add_implicit_indentation</span><span class="p">()</span>
<span class="nx">@add_implicit_parentheses</span><span class="p">()</span>
<span class="nx">@ensure_balance</span> <span class="nx">BALANCED_PAIRS</span>
<span class="nx">@rewrite_closing_parens</span><span class="p">()</span>
<span class="nx">@tokens</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p>Rewrite the token stream, looking one token ahead and behind.
Allow the return value of the block to tell us how many tokens to move
forwards (or backwards) in the stream, to make sure we don't miss anything
as tokens are inserted and removed, and the stream changes length under
our feet.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">scan_tokens: </span><span class="p">(</span><span class="nx">block</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">i: </span><span class="mi">0</span>
<span class="k">while</span> <span class="kc">true</span>
<span class="k">break</span> <span class="nx">unless</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span>
<span class="nv">move: </span><span class="nx">block</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">],</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">],</span> <span class="nx">i</span>
<span class="nv">i: </span><span class="o">+</span> <span class="nx">move</span>
<span class="kc">true</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>Massage newlines and indentations so that comments don't have to be
correctly indented, or appear on a line of their own.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">adjust_comments: </span><span class="o">-&gt;</span>
<span class="nx">@scan_tokens</span> <span class="p">(</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">token</span><span class="p">,</span> <span class="nx">post</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span>
<span class="k">return</span> <span class="mi">1</span> <span class="nx">unless</span> <span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;COMMENT&#39;</span>
<span class="p">[</span><span class="nx">before</span><span class="p">,</span> <span class="nx">after</span><span class="p">]</span><span class="o">:</span> <span class="p">[</span><span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span> <span class="o">-</span> <span class="mi">2</span><span class="p">],</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]]</span>
<span class="k">if</span> <span class="nx">after</span> <span class="o">and</span> <span class="nx">after</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;INDENT&#39;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span>
<span class="k">if</span> <span class="nx">before</span> <span class="o">and</span> <span class="nx">before</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;OUTDENT&#39;</span> <span class="o">and</span> <span class="nx">post</span> <span class="o">and</span> <span class="nx">prev</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="nx">post</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;TERMINATOR&#39;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span> <span class="o">-</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span>
<span class="k">else</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">after</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">prev</span> <span class="o">and</span> <span class="nx">prev</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">isnt</span> <span class="s1">&#39;TERMINATOR&#39;</span> <span class="o">and</span> <span class="nx">prev</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">isnt</span> <span class="s1">&#39;INDENT&#39;</span> <span class="o">and</span> <span class="nx">prev</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">isnt</span> <span class="s1">&#39;OUTDENT&#39;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;TERMINATOR&#39;</span><span class="p">,</span> <span class="s2">&quot;\n&quot;</span><span class="p">,</span> <span class="nx">prev</span><span class="p">[</span><span class="mi">2</span><span class="p">]]</span>
<span class="k">return</span> <span class="mi">2</span>
<span class="k">else</span>
<span class="k">return</span> <span class="mi">1</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>Leading newlines would introduce an ambiguity in the grammar, so we
dispatch them here.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">remove_leading_newlines: </span><span class="o">-&gt;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">shift</span><span class="p">()</span> <span class="k">while</span> <span class="nx">@tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">and</span> <span class="nx">@tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;TERMINATOR&#39;</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>Some blocks occur in the middle of expressions -- when we're expecting
this, remove their trailing newlines.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">remove_mid_expression_newlines: </span><span class="o">-&gt;</span>
<span class="nx">@scan_tokens</span> <span class="p">(</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">token</span><span class="p">,</span> <span class="nx">post</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span>
<span class="k">return</span> <span class="mi">1</span> <span class="nx">unless</span> <span class="nx">post</span> <span class="o">and</span> <span class="nx">include</span><span class="p">(</span><span class="nx">EXPRESSION_CLOSE</span><span class="p">,</span> <span class="nx">post</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">and</span> <span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;TERMINATOR&#39;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span><span class="p">,</span> <span class="mi">1</span>
<span class="k">return</span> <span class="mi">0</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>The lexer has tagged the opening parenthesis of a method call, and the
opening bracket of an indexing operation. Match them with their paired
close.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">close_open_calls_and_indexes: </span><span class="o">-&gt;</span>
<span class="nv">parens: </span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nv">brackets: </span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nx">@scan_tokens</span> <span class="p">(</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">token</span><span class="p">,</span> <span class="nx">post</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span>
<span class="k">switch</span> <span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">when</span> <span class="s1">&#39;CALL_START&#39;</span> <span class="k">then</span> <span class="nx">parens</span><span class="p">.</span><span class="nx">push</span> <span class="mi">0</span>
<span class="k">when</span> <span class="s1">&#39;INDEX_START&#39;</span> <span class="k">then</span> <span class="nx">brackets</span><span class="p">.</span><span class="nx">push</span> <span class="mi">0</span>
<span class="k">when</span> <span class="s1">&#39;(&#39;</span> <span class="k">then</span> <span class="nx">parens</span><span class="p">[</span><span class="nx">parens</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span><span class="o">:</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">when</span> <span class="s1">&#39;[&#39;</span> <span class="k">then</span> <span class="nx">brackets</span><span class="p">[</span><span class="nx">brackets</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span><span class="o">:</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">when</span> <span class="s1">&#39;)&#39;</span>
<span class="k">if</span> <span class="nx">parens</span><span class="p">[</span><span class="nx">parens</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">is</span> <span class="mi">0</span>
<span class="nx">parens</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;CALL_END&#39;</span>
<span class="k">else</span>
<span class="nx">parens</span><span class="p">[</span><span class="nx">parens</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span><span class="o">:</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">when</span> <span class="s1">&#39;]&#39;</span>
<span class="k">if</span> <span class="nx">brackets</span><span class="p">[</span><span class="nx">brackets</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span>
<span class="nx">brackets</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;INDEX_END&#39;</span>
<span class="k">else</span>
<span class="nx">brackets</span><span class="p">[</span><span class="nx">brackets</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span><span class="o">:</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">return</span> <span class="mi">1</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-11">#</a> </div> <p>Methods may be optionally called without parentheses, for simple cases.
Insert the implicit parentheses here, so that the parser doesn't have to
deal with them.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">add_implicit_parentheses: </span><span class="o">-&gt;</span>
<span class="nv">stack: </span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nv">close_calls: </span><span class="p">(</span><span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span>
<span class="k">for</span> <span class="nx">tmp</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">...</span><span class="nx">stack</span><span class="p">[</span><span class="nx">stack</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]]</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;CALL_END&#39;</span><span class="p">,</span> <span class="s1">&#39;)&#39;</span><span class="p">,</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span><span class="p">][</span><span class="mi">2</span><span class="p">]])</span>
<span class="nv">size: </span><span class="nx">stack</span><span class="p">[</span><span class="nx">stack</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
<span class="nx">stack</span><span class="p">[</span><span class="nx">stack</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span><span class="o">:</span> <span class="mi">0</span>
<span class="nx">size</span>
<span class="nx">@scan_tokens</span> <span class="p">(</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">token</span><span class="p">,</span> <span class="nx">post</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span>
<span class="nv">tag: </span><span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nx">stack</span><span class="p">[</span><span class="nx">stack</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span><span class="o">:</span> <span class="o">+</span> <span class="nx">stack</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span> <span class="k">if</span> <span class="nx">tag</span> <span class="o">is</span> <span class="s1">&#39;OUTDENT&#39;</span>
<span class="nv">open: </span><span class="nx">stack</span><span class="p">[</span><span class="nx">stack</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span>
<span class="k">if</span> <span class="nx">prev</span> <span class="o">and</span> <span class="nx">prev</span><span class="p">.</span><span class="nx">spaced</span> <span class="o">and</span> <span class="nx">include</span><span class="p">(</span><span class="nx">IMPLICIT_FUNC</span><span class="p">,</span> <span class="nx">prev</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">and</span> <span class="nx">include</span><span class="p">(</span><span class="nx">IMPLICIT_CALL</span><span class="p">,</span> <span class="nx">tag</span><span class="p">)</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;CALL_START&#39;</span><span class="p">,</span> <span class="s1">&#39;(&#39;</span><span class="p">,</span> <span class="nx">token</span><span class="p">[</span><span class="mi">2</span><span class="p">]]</span>
<span class="nx">stack</span><span class="p">[</span><span class="nx">stack</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span><span class="o">:</span> <span class="o">+</span> <span class="mi">1</span>
<span class="nx">stack</span><span class="p">.</span><span class="nx">push</span> <span class="mi">0</span> <span class="k">if</span> <span class="nx">include</span><span class="p">(</span><span class="nx">EXPRESSION_START</span><span class="p">,</span> <span class="nx">tag</span><span class="p">)</span>
<span class="k">return</span> <span class="mi">2</span>
<span class="k">if</span> <span class="nx">include</span><span class="p">(</span><span class="nx">EXPRESSION_START</span><span class="p">,</span> <span class="nx">tag</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">tag</span> <span class="o">is</span> <span class="s1">&#39;INDENT&#39;</span> <span class="o">and</span> <span class="o">!</span><span class="nx">token</span><span class="p">.</span><span class="nx">generated</span> <span class="o">and</span> <span class="nx">open</span> <span class="o">and</span> <span class="o">not</span> <span class="p">(</span><span class="nx">prev</span> <span class="o">and</span> <span class="nx">include</span><span class="p">(</span><span class="nx">IMPLICIT_BLOCK</span><span class="p">,</span> <span class="nx">prev</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
<span class="nv">size: </span><span class="nx">close_calls</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span>
<span class="nx">stack</span><span class="p">.</span><span class="nx">push</span> <span class="mi">0</span>
<span class="k">return</span> <span class="nx">size</span>
<span class="nx">stack</span><span class="p">.</span><span class="nx">push</span> <span class="mi">0</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">if</span> <span class="nx">open</span> <span class="o">and</span> <span class="o">!</span><span class="nx">token</span><span class="p">.</span><span class="nx">generated</span> <span class="o">and</span> <span class="p">(</span><span class="o">!</span><span class="nx">post</span> <span class="o">or</span> <span class="nx">include</span><span class="p">(</span><span class="nx">IMPLICIT_END</span><span class="p">,</span> <span class="nx">tag</span><span class="p">))</span>
<span class="nv">j: </span><span class="mi">1</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span> <span class="k">while</span> <span class="p">(</span><span class="nv">nx: </span><span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span> <span class="o">+</span> <span class="nx">j</span><span class="p">])</span><span class="o">?</span> <span class="o">and</span> <span class="nx">include</span><span class="p">(</span><span class="nx">IMPLICIT_END</span><span class="p">,</span> <span class="nx">nx</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">if</span> <span class="nx">nx</span><span class="o">?</span> <span class="o">and</span> <span class="nx">nx</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;,&#39;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="k">if</span> <span class="nx">tag</span> <span class="o">is</span> <span class="s1">&#39;TERMINATOR&#39;</span>
<span class="k">else</span>
<span class="nv">size: </span><span class="nx">close_calls</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span>
<span class="nx">stack</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span> <span class="k">if</span> <span class="nx">tag</span> <span class="o">isnt</span> <span class="s1">&#39;OUTDENT&#39;</span> <span class="o">and</span> <span class="nx">include</span> <span class="nx">EXPRESSION_END</span><span class="p">,</span> <span class="nx">tag</span>
<span class="k">return</span> <span class="nx">size</span>
<span class="k">if</span> <span class="nx">tag</span> <span class="o">isnt</span> <span class="s1">&#39;OUTDENT&#39;</span> <span class="o">and</span> <span class="nx">include</span> <span class="nx">EXPRESSION_END</span><span class="p">,</span> <span class="nx">tag</span>
<span class="nx">stack</span><span class="p">[</span><span class="nx">stack</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span><span class="o">:</span> <span class="o">+</span> <span class="nx">stack</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">return</span> <span class="mi">1</span></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-12">#</a> </div> <p>Because our grammar is LALR(1), it can't handle some single-line
expressions that lack ending delimiters. The <strong>Rewriter</strong> 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.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">add_implicit_indentation: </span><span class="o">-&gt;</span>
<span class="nx">@scan_tokens</span> <span class="p">(</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">token</span><span class="p">,</span> <span class="nx">post</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span>
<span class="k">return</span> <span class="mi">1</span> <span class="nx">unless</span> <span class="nx">include</span><span class="p">(</span><span class="nx">SINGLE_LINERS</span><span class="p">,</span> <span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">and</span>
<span class="nx">post</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">isnt</span> <span class="s1">&#39;INDENT&#39;</span> <span class="o">and</span>
<span class="o">not</span> <span class="p">(</span><span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;ELSE&#39;</span> <span class="o">and</span> <span class="nx">post</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;IF&#39;</span><span class="p">)</span>
<span class="nv">starter: </span><span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nv">indent: </span><span class="p">[</span><span class="s1">&#39;INDENT&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">token</span><span class="p">[</span><span class="mi">2</span><span class="p">]]</span>
<span class="nv">indent.generated: </span><span class="kc">true</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">indent</span>
<span class="nv">idx: </span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="nv">parens: </span><span class="mi">0</span>
<span class="k">while</span> <span class="kc">true</span>
<span class="nv">idx: </span><span class="o">+</span> <span class="mi">1</span>
<span class="nv">tok: </span><span class="nx">@tokens</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span>
<span class="nv">pre: </span><span class="nx">@tokens</span><span class="p">[</span><span class="nx">idx</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span><span class="o">not</span> <span class="nx">tok</span> <span class="o">or</span>
<span class="p">(</span><span class="nx">include</span><span class="p">(</span><span class="nx">SINGLE_CLOSERS</span><span class="p">,</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">and</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">isnt</span> <span class="s1">&#39;;&#39;</span><span class="p">)</span> <span class="o">or</span>
<span class="p">(</span><span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;)&#39;</span> <span class="o">and</span> <span class="nx">parens</span> <span class="o">is</span> <span class="mi">0</span><span class="p">))</span> <span class="o">and</span>
<span class="o">not</span> <span class="p">(</span><span class="nx">starter</span> <span class="o">is</span> <span class="s1">&#39;ELSE&#39;</span> <span class="o">and</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;ELSE&#39;</span><span class="p">)</span>
<span class="nv">insertion: </span><span class="k">if</span> <span class="nx">pre</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s2">&quot;,&quot;</span> <span class="k">then</span> <span class="nx">idx</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">else</span> <span class="nx">idx</span>
<span class="nv">outdent: </span><span class="p">[</span><span class="s1">&#39;OUTDENT&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">token</span><span class="p">[</span><span class="mi">2</span><span class="p">]]</span>
<span class="nv">outdent.generated: </span><span class="kc">true</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">insertion</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">outdent</span>
<span class="k">break</span>
<span class="nv">parens: </span><span class="o">+</span> <span class="mi">1</span> <span class="k">if</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;(&#39;</span>
<span class="nv">parens: </span><span class="o">-</span> <span class="mi">1</span> <span class="k">if</span> <span class="nx">tok</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;)&#39;</span>
<span class="k">return</span> <span class="mi">1</span> <span class="nx">unless</span> <span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="s1">&#39;THEN&#39;</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span><span class="p">,</span> <span class="mi">1</span>
<span class="k">return</span> <span class="mi">0</span></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-13">#</a> </div> <p>Ensure that all listed pairs of tokens are correctly balanced throughout
the course of the token stream.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">ensure_balance: </span><span class="p">(</span><span class="nx">pairs</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nv">levels: </span><span class="p">{}</span>
<span class="nv">open_line: </span><span class="p">{}</span>
<span class="nx">@scan_tokens</span> <span class="p">(</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">token</span><span class="p">,</span> <span class="nx">post</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span>
<span class="k">for</span> <span class="nx">pair</span> <span class="k">in</span> <span class="nx">pairs</span>
<span class="p">[</span><span class="nx">open</span><span class="p">,</span> <span class="nx">close</span><span class="p">]</span><span class="o">:</span> <span class="nx">pair</span>
<span class="nx">levels</span><span class="p">[</span><span class="nx">open</span><span class="p">]</span><span class="o">:</span> <span class="o">or</span> <span class="mi">0</span>
<span class="k">if</span> <span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="nx">open</span>
<span class="nx">open_line</span><span class="p">[</span><span class="nx">open</span><span class="p">]</span><span class="o">:</span> <span class="nx">token</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="k">if</span> <span class="nx">levels</span><span class="p">[</span><span class="nx">open</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span>
<span class="nx">levels</span><span class="p">[</span><span class="nx">open</span><span class="p">]</span><span class="o">:</span> <span class="o">+</span> <span class="mi">1</span>
<span class="nx">levels</span><span class="p">[</span><span class="nx">open</span><span class="p">]</span><span class="o">:</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">if</span> <span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="nx">close</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&quot;too many ${token[1]} on line ${token[2] + 1}&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="nx">levels</span><span class="p">[</span><span class="nx">open</span><span class="p">]</span> <span class="o">&lt;</span> <span class="mi">0</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="nv">unclosed: </span><span class="nx">key</span> <span class="k">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">value</span> <span class="k">of</span> <span class="nx">levels</span> <span class="k">when</span> <span class="nx">value</span> <span class="o">&gt;</span> <span class="mi">0</span>
<span class="k">if</span> <span class="nx">unclosed</span><span class="p">.</span><span class="nx">length</span>
<span class="nv">open: </span><span class="nx">unclosed</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nv">line: </span><span class="nx">open_line</span><span class="p">[</span><span class="nx">open</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span> <span class="s2">&quot;unclosed $open on line $line&quot;</span></pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-14">#</a> </div> <p>We'd like to support syntax like this:</p>
<pre><code>el.click((event) -&gt;
el.hide())
</code></pre>
<p>In order to accomplish this, move outdents that follow closing parens
inwards, safely. The steps to accomplish this are:</p>
<ol>
<li>Check that all paired tokens are balanced and in order.</li>
<li>Rewrite the stream with a stack: if you see an <code>EXPRESSION_START</code>, add it
to the stack. If you see an <code>EXPRESSION_END</code>, pop the stack and replace
it with the inverse of what we've just popped.</li>
<li>Keep track of "debt" for tokens that we manufacture, to make sure we end
up balanced in the end.</li>
<li>Be careful not to alter array or parentheses delimiters with overzealous
rewriting.</li>
</ol> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">rewrite_closing_parens: </span><span class="o">-&gt;</span>
<span class="nv">stack: </span><span class="p">[]</span>
<span class="nv">debt: </span> <span class="p">{}</span>
<span class="p">(</span><span class="nx">debt</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span><span class="o">:</span> <span class="mi">0</span><span class="p">)</span> <span class="k">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">val</span> <span class="k">of</span> <span class="nx">INVERSES</span>
<span class="nx">@scan_tokens</span> <span class="p">(</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">token</span><span class="p">,</span> <span class="nx">post</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span>
<span class="nv">tag: </span><span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nv">inv: </span><span class="nx">INVERSES</span><span class="p">[</span><span class="nx">token</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
<span class="k">if</span> <span class="nx">include</span> <span class="nx">EXPRESSION_START</span><span class="p">,</span> <span class="nx">tag</span>
<span class="nx">stack</span><span class="p">.</span><span class="nx">push</span> <span class="nx">token</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">else</span> <span class="k">if</span> <span class="nx">include</span> <span class="nx">EXPRESSION_END</span><span class="p">,</span> <span class="nx">tag</span>
<span class="k">if</span> <span class="nx">debt</span><span class="p">[</span><span class="nx">inv</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span>
<span class="nx">debt</span><span class="p">[</span><span class="nx">inv</span><span class="p">]</span><span class="o">:</span> <span class="o">-</span> <span class="mi">1</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span><span class="p">,</span> <span class="mi">1</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">else</span>
<span class="nv">match: </span><span class="nx">stack</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
<span class="nv">mtag: </span> <span class="nx">match</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nv">oppos: </span><span class="nx">INVERSES</span><span class="p">[</span><span class="nx">mtag</span><span class="p">]</span>
<span class="k">return</span> <span class="mi">1</span> <span class="k">if</span> <span class="nx">tag</span> <span class="o">is</span> <span class="nx">oppos</span>
<span class="nx">debt</span><span class="p">[</span><span class="nx">mtag</span><span class="p">]</span><span class="o">:</span> <span class="o">+</span> <span class="mi">1</span>
<span class="nv">val: </span><span class="p">[</span><span class="nx">oppos</span><span class="p">,</span> <span class="k">if</span> <span class="nx">mtag</span> <span class="o">is</span> <span class="s1">&#39;INDENT&#39;</span> <span class="k">then</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">else</span> <span class="nx">oppos</span><span class="p">]</span>
<span class="k">if</span> <span class="nx">@tokens</span><span class="p">[</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span><span class="o">?</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">is</span> <span class="nx">mtag</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">val</span>
<span class="nx">stack</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">match</span><span class="p">)</span>
<span class="k">else</span>
<span class="nx">@tokens</span><span class="p">.</span><span class="nx">splice</span> <span class="nx">i</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">val</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">else</span>
<span class="k">return</span> <span class="mi">1</span></pre></div> </td> </tr> <tr id="section-15"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-15">#</a> </div> <h2>Constants</h2> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-16"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-16">#</a> </div> <p>List of the token pairs that must be balanced.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">BALANCED_PAIRS: </span><span class="p">[[</span><span class="s1">&#39;(&#39;</span><span class="p">,</span> <span class="s1">&#39;)&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;[&#39;</span><span class="p">,</span> <span class="s1">&#39;]&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;{&#39;</span><span class="p">,</span> <span class="s1">&#39;}&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;INDENT&#39;</span><span class="p">,</span> <span class="s1">&#39;OUTDENT&#39;</span><span class="p">],</span>
<span class="p">[</span><span class="s1">&#39;PARAM_START&#39;</span><span class="p">,</span> <span class="s1">&#39;PARAM_END&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;CALL_START&#39;</span><span class="p">,</span> <span class="s1">&#39;CALL_END&#39;</span><span class="p">],</span>
<span class="p">[</span><span class="s1">&#39;INDEX_START&#39;</span><span class="p">,</span> <span class="s1">&#39;INDEX_END&#39;</span><span class="p">],</span> <span class="p">[</span><span class="s1">&#39;SOAKED_INDEX_START&#39;</span><span class="p">,</span> <span class="s1">&#39;SOAKED_INDEX_END&#39;</span><span class="p">]]</span></pre></div> </td> </tr> <tr id="section-17"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-17">#</a> </div> <p>The inverse mappings of <code>BALANCED_PAIRS</code> we're trying to fix up, so we can
look things up from either end.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">INVERSES: </span><span class="p">{}</span>
<span class="k">for</span> <span class="nx">pair</span> <span class="k">in</span> <span class="nx">BALANCED_PAIRS</span>
<span class="nx">INVERSES</span><span class="p">[</span><span class="nx">pair</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span><span class="o">:</span> <span class="nx">pair</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="nx">INVERSES</span><span class="p">[</span><span class="nx">pair</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span><span class="o">:</span> <span class="nx">pair</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-18"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-18">#</a> </div> <p>The tokens that signal the start of a balanced pair.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">EXPRESSION_START: </span><span class="nx">pair</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="nx">pair</span> <span class="k">in</span> <span class="nx">BALANCED_PAIRS</span></pre></div> </td> </tr> <tr id="section-19"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-19">#</a> </div> <p>The tokens that signal the end of a balanced pair.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">EXPRESSION_END: </span> <span class="nx">pair</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="nx">pair</span> <span class="k">in</span> <span class="nx">BALANCED_PAIRS</span></pre></div> </td> </tr> <tr id="section-20"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-20">#</a> </div> <p>Tokens that indicate the close of a clause of an expression.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">EXPRESSION_CLOSE: </span><span class="p">[</span><span class="s1">&#39;CATCH&#39;</span><span class="p">,</span> <span class="s1">&#39;WHEN&#39;</span><span class="p">,</span> <span class="s1">&#39;ELSE&#39;</span><span class="p">,</span> <span class="s1">&#39;FINALLY&#39;</span><span class="p">].</span><span class="nx">concat</span> <span class="nx">EXPRESSION_END</span></pre></div> </td> </tr> <tr id="section-21"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-21">#</a> </div> <p>Tokens that, if followed by an <code>IMPLICIT_CALL</code>, indicate a function invocation.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">IMPLICIT_FUNC: </span> <span class="p">[</span><span class="s1">&#39;IDENTIFIER&#39;</span><span class="p">,</span> <span class="s1">&#39;SUPER&#39;</span><span class="p">,</span> <span class="s1">&#39;)&#39;</span><span class="p">,</span> <span class="s1">&#39;CALL_END&#39;</span><span class="p">,</span> <span class="s1">&#39;]&#39;</span><span class="p">,</span> <span class="s1">&#39;INDEX_END&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt;-&#39;</span><span class="p">,</span> <span class="s1">&#39;@&#39;</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-22"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-22">#</a> </div> <p>If preceded by an <code>IMPLICIT_FUNC</code>, indicates a function invocation.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">IMPLICIT_CALL: </span> <span class="p">[</span><span class="s1">&#39;IDENTIFIER&#39;</span><span class="p">,</span> <span class="s1">&#39;NUMBER&#39;</span><span class="p">,</span> <span class="s1">&#39;STRING&#39;</span><span class="p">,</span> <span class="s1">&#39;JS&#39;</span><span class="p">,</span> <span class="s1">&#39;REGEX&#39;</span><span class="p">,</span> <span class="s1">&#39;NEW&#39;</span><span class="p">,</span> <span class="s1">&#39;PARAM_START&#39;</span><span class="p">,</span>
<span class="s1">&#39;TRY&#39;</span><span class="p">,</span> <span class="s1">&#39;DELETE&#39;</span><span class="p">,</span> <span class="s1">&#39;TYPEOF&#39;</span><span class="p">,</span> <span class="s1">&#39;SWITCH&#39;</span><span class="p">,</span> <span class="s1">&#39;EXTENSION&#39;</span><span class="p">,</span>
<span class="s1">&#39;TRUE&#39;</span><span class="p">,</span> <span class="s1">&#39;FALSE&#39;</span><span class="p">,</span> <span class="s1">&#39;YES&#39;</span><span class="p">,</span> <span class="s1">&#39;NO&#39;</span><span class="p">,</span> <span class="s1">&#39;ON&#39;</span><span class="p">,</span> <span class="s1">&#39;OFF&#39;</span><span class="p">,</span> <span class="s1">&#39;!&#39;</span><span class="p">,</span> <span class="s1">&#39;!!&#39;</span><span class="p">,</span> <span class="s1">&#39;NOT&#39;</span><span class="p">,</span>
<span class="s1">&#39;THIS&#39;</span><span class="p">,</span> <span class="s1">&#39;NULL&#39;</span><span class="p">,</span>
<span class="s1">&#39;@&#39;</span><span class="p">,</span> <span class="s1">&#39;-&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;=&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;[&#39;</span><span class="p">,</span> <span class="s1">&#39;(&#39;</span><span class="p">,</span> <span class="s1">&#39;{&#39;</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-23"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-23">#</a> </div> <p>Tokens indicating that the implicit call must enclose a block of expressions.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">IMPLICIT_BLOCK: </span><span class="p">[</span><span class="s1">&#39;-&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;=&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;{&#39;</span><span class="p">,</span> <span class="s1">&#39;[&#39;</span><span class="p">,</span> <span class="s1">&#39;,&#39;</span><span class="p">]</span></pre></div> </td> </tr> <tr id="section-24"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-24">#</a> </div> <p>Tokens that always mark the end of an implicit call for single-liners.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">IMPLICIT_END: </span> <span class="p">[</span><span class="s1">&#39;IF&#39;</span><span class="p">,</span> <span class="s1">&#39;UNLESS&#39;</span><span class="p">,</span> <span class="s1">&#39;FOR&#39;</span><span class="p">,</span> <span class="s1">&#39;WHILE&#39;</span><span class="p">,</span> <span class="s1">&#39;UNTIL&#39;</span><span class="p">,</span> <span class="s1">&#39;TERMINATOR&#39;</span><span class="p">,</span> <span class="s1">&#39;INDENT&#39;</span><span class="p">].</span><span class="nx">concat</span> <span class="nx">EXPRESSION_END</span></pre></div> </td> </tr> <tr id="section-25"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-25">#</a> </div> <p>Single-line flavors of block expressions that have unclosed endings.
The grammar can't disambiguate them, so we insert the implicit indentation.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">SINGLE_LINERS: </span><span class="p">[</span><span class="s1">&#39;ELSE&#39;</span><span class="p">,</span> <span class="s2">&quot;-&gt;&quot;</span><span class="p">,</span> <span class="s2">&quot;=&gt;&quot;</span><span class="p">,</span> <span class="s1">&#39;TRY&#39;</span><span class="p">,</span> <span class="s1">&#39;FINALLY&#39;</span><span class="p">,</span> <span class="s1">&#39;THEN&#39;</span><span class="p">]</span>
<span class="nv">SINGLE_CLOSERS: </span><span class="p">[</span><span class="s1">&#39;TERMINATOR&#39;</span><span class="p">,</span> <span class="s1">&#39;CATCH&#39;</span><span class="p">,</span> <span class="s1">&#39;FINALLY&#39;</span><span class="p">,</span> <span class="s1">&#39;ELSE&#39;</span><span class="p">,</span> <span class="s1">&#39;OUTDENT&#39;</span><span class="p">,</span> <span class="s1">&#39;LEADING_WHEN&#39;</span><span class="p">]</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@@ -0,0 +1,48 @@
<!DOCTYPE html> <html> <head> <title>scope.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="cake.html"> cake.coffee </a> <a class="source" href="coffee-script.html"> coffee-script.coffee </a> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="grammar.html"> grammar.coffee </a> <a class="source" href="helpers.html"> helpers.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="lexer.html"> lexer.coffee </a> <a class="source" href="nodes.html"> nodes.coffee </a> <a class="source" href="optparse.html"> optparse.coffee </a> <a class="source" href="repl.html"> repl.coffee </a> <a class="source" href="rewriter.html"> rewriter.coffee </a> <a class="source" href="scope.html"> scope.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> scope.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> <p>The <strong>Scope</strong> class regulates lexical scoping within CoffeeScript. As you
generate code, you create a tree of scopes in the same shape as the nested
function bodies. Each scope knows about the variables declared within it,
and has a reference to its parent enclosing scope. In this way, we know which
variables are new and need to be declared with <code>var</code>, and which are shared
with the outside.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>Set up exported variables for both <strong>Node.js</strong> and the browser.</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">this</span><span class="p">.</span><span class="nv">exports: </span><span class="k">this</span> <span class="nx">unless</span> <span class="nx">process</span><span class="o">?</span>
<span class="nv">exports.Scope: </span><span class="nx">class</span> <span class="nx">Scope</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <p>The top-level <strong>Scope</strong> object.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="vi">@root: </span><span class="kc">null</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>Initialize a scope with its parent, for lookups up the chain,
as well as a reference to the <strong>Expressions</strong> node is belongs to, which is
where it should declare its variables, and a reference to the function that
it wraps.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">constructor: </span><span class="p">(</span><span class="nx">parent</span><span class="p">,</span> <span class="nx">expressions</span><span class="p">,</span> <span class="nx">method</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="p">[</span><span class="nx">@parent</span><span class="p">,</span> <span class="nx">@expressions</span><span class="p">,</span> <span class="nx">@method</span><span class="p">]</span><span class="o">:</span> <span class="p">[</span><span class="nx">parent</span><span class="p">,</span> <span class="nx">expressions</span><span class="p">,</span> <span class="nx">method</span><span class="p">]</span>
<span class="vi">@variables: </span><span class="p">{}</span>
<span class="k">if</span> <span class="nx">@parent</span>
<span class="vi">@temp_var: </span><span class="nx">@parent</span><span class="p">.</span><span class="nx">temp_var</span>
<span class="k">else</span>
<span class="nv">Scope.root: </span><span class="k">this</span>
<span class="vi">@temp_var: </span><span class="s1">&#39;_a&#39;</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>Look up a variable name in lexical scope, and declare it if it does not
already exist.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">find: </span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">true</span> <span class="k">if</span> <span class="nx">@check</span> <span class="nx">name</span>
<span class="nx">@variables</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;var&#39;</span>
<span class="kc">false</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <p>Test variables and return true the first time fn(v, k) returns true</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">any: </span><span class="p">(</span><span class="nx">fn</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">for</span> <span class="nx">v</span><span class="p">,</span> <span class="nx">k</span> <span class="k">of</span> <span class="nx">@variables</span> <span class="k">when</span> <span class="nx">fn</span><span class="p">(</span><span class="nx">v</span><span class="p">,</span> <span class="nx">k</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="k">return</span> <span class="kc">false</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>Reserve a variable name as originating from a function parameter for this
scope. No <code>var</code> required for internal references.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">parameter: </span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">@variables</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;param&#39;</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>Just check to see if a variable has already been declared, without reserving.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">check: </span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="k">return</span> <span class="kc">true</span> <span class="k">if</span> <span class="nx">@variables</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span>
<span class="o">!!</span><span class="p">(</span><span class="nx">@parent</span> <span class="o">and</span> <span class="nx">@parent</span><span class="p">.</span><span class="nx">check</span><span class="p">(</span><span class="nx">name</span><span class="p">))</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <p>If we need to store an intermediate result, find an available name for a
compiler-generated variable. <code>_a</code>, <code>_b</code>, and so on...</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">free_variable: </span><span class="o">-&gt;</span>
<span class="k">while</span> <span class="nx">@check</span> <span class="nx">@temp_var</span>
<span class="nv">ordinal: </span><span class="mi">1</span> <span class="o">+</span> <span class="nb">parseInt</span> <span class="nx">@temp_var</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="mi">36</span>
<span class="vi">@temp_var: </span><span class="s1">&#39;_&#39;</span> <span class="o">+</span> <span class="nx">ordinal</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">36</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\d/g</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">)</span>
<span class="nx">@variables</span><span class="p">[</span><span class="nx">@temp_var</span><span class="p">]</span><span class="o">:</span> <span class="s1">&#39;var&#39;</span>
<span class="nx">@temp_var</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>Ensure that an assignment is made at the top of this scope
(or at the top-level scope, if requested).</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">assign: </span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">@variables</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span><span class="o">:</span> <span class="p">{</span><span class="nv">value: </span><span class="nx">value</span><span class="p">,</span> <span class="nv">assigned: </span><span class="kc">true</span><span class="p">}</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-11">#</a> </div> <p>Does this scope reference any variables that need to be declared in the
given function body?</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">has_declarations: </span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">body</span> <span class="o">is</span> <span class="nx">@expressions</span> <span class="o">and</span> <span class="nx">@any</span> <span class="p">(</span><span class="nx">k</span><span class="p">,</span> <span class="nx">val</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nx">val</span> <span class="o">is</span> <span class="s1">&#39;var&#39;</span></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-12">#</a> </div> <p>Does this scope reference any assignments that need to be declared at the
top of the given function body?</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">has_assignments: </span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> <span class="o">-&gt;</span>
<span class="nx">body</span> <span class="o">is</span> <span class="nx">@expressions</span> <span class="o">and</span> <span class="nx">@any</span> <span class="p">(</span><span class="nx">k</span><span class="p">,</span> <span class="nx">val</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nx">val</span><span class="p">.</span><span class="nx">assigned</span></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-13">#</a> </div> <p>Return the list of variables first declared in this scope.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">declared_variables: </span><span class="o">-&gt;</span>
<span class="p">(</span><span class="nx">key</span> <span class="k">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">val</span> <span class="k">of</span> <span class="nx">@variables</span> <span class="k">when</span> <span class="nx">val</span> <span class="o">is</span> <span class="s1">&#39;var&#39;</span><span class="p">).</span><span class="nx">sort</span><span class="p">()</span></pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-14">#</a> </div> <p>Return the list of assignments that are supposed to be made at the top
of this scope.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">assigned_variables: </span><span class="o">-&gt;</span>
<span class="s2">&quot;$key = $val.value&quot;</span> <span class="k">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">val</span> <span class="k">of</span> <span class="nx">@variables</span> <span class="k">when</span> <span class="nx">val</span><span class="p">.</span><span class="nx">assigned</span></pre></div> </td> </tr> <tr id="section-15"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-15">#</a> </div> <p>Compile the JavaScript for all of the variable declarations in this scope.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">compiled_declarations: </span><span class="o">-&gt;</span>
<span class="nx">@declared_variables</span><span class="p">().</span><span class="nx">join</span> <span class="s1">&#39;, &#39;</span></pre></div> </td> </tr> <tr id="section-16"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-16">#</a> </div> <p>Compile the JavaScript for all of the variable assignments in this scope.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nv">compiled_assignments: </span><span class="o">-&gt;</span>
<span class="nx">@assigned_variables</span><span class="p">().</span><span class="nx">join</span> <span class="s1">&#39;, &#39;</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

File diff suppressed because one or more lines are too long

View File

@@ -30,7 +30,7 @@
<div id="flybar">
<a id="logo" href="#top"> </a>
<div class="navigation">
<div class="navigation toc">
<div class="button">
Table of Contents
</div>
@@ -46,8 +46,7 @@
<a href="#conditionals">Conditionals, Ternaries, and Conditional Assignment</a>
<a href="#aliases">Aliases</a>
<a href="#splats">Splats...</a>
<a href="#arguments">Arguments are Arrays</a>
<a href="#while">While Loops</a>
<a href="#while">While &amp; Until Loops</a>
<a href="#comprehensions">Comprehensions (Arrays, Objects, and Ranges)</a>
<a href="#slice_splice">Array Slicing and Splicing with Ranges</a>
<a href="#expressions">Everything is an Expression</a>
@@ -59,10 +58,12 @@
<a href="#switch">Switch/When/Else</a>
<a href="#try">Try/Catch/Finally</a>
<a href="#comparisons">Chained Comparisons</a>
<a href="#interpolation">String and RegExp Interpolation</a>
<a href="#strings">Multiline Strings and Heredocs</a>
<a href="#cake">Cake, and Cakefiles</a>
<a href="#scripts">"text/coffeescript" Script Tags</a>
<a href="#resources">Resources</a>
<a href="#webchat">Web Chat (IRC)</a>
<a href="#change_log">Change Log</a>
</div>
</div>
@@ -75,7 +76,7 @@
<div id="repl_source_wrap"><textarea id="repl_source">reverse: (string) ->
string.split('').reverse().join ''
alert reverse '!tpircseeffoC'</textarea></div>
alert reverse '.eeffoC yrT'</textarea></div>
<pre id="repl_results"></pre>
<button class="full_screen">go full screen</button>
<button class="minimize">minimize</button>
@@ -84,6 +85,24 @@ alert reverse '!tpircseeffoC'</textarea></div>
</div>
</div>
</div>
<div class="navigation annotated">
<div class="button">
Annotated Source
</div>
<div class="contents">
<a href="documentation/docs/grammar.html">Grammar Rules &mdash; src/grammar</a>
<a href="documentation/docs/lexer.html">Lexing Tokens &mdash; src/lexer</a>
<a href="documentation/docs/rewriter.html">The Rewriter &mdash; src/rewriter</a>
<a href="documentation/docs/nodes.html">The Syntax Tree &mdash; src/nodes</a>
<a href="documentation/docs/scope.html">Lexical Scope &mdash; src/scope</a>
<a href="documentation/docs/helpers.html">Helpers &amp; Utility Functions &mdash; src/helpers</a>
<a href="documentation/docs/coffee-script.html">The CoffeeScript Module &mdash; src/coffee-script</a>
<a href="documentation/docs/cake.html">Cake &amp; Cakefiles &mdash; src/cake</a>
<a href="documentation/docs/command.html">"coffee" Command-Line Utility &mdash; src/command</a>
<a href="documentation/docs/optparse.html">Option Parsing &mdash; src/optparse</a>
<a href="documentation/docs/repl.html">Interactive REPL &mdash; src/repl</a>
</div>
</div>
<div id="error" style="display:none;"></div>
</div>
@@ -111,7 +130,7 @@ alert reverse '!tpircseeffoC'</textarea></div>
<p>
<b>Latest Version:</b>
<a href="http://github.com/jashkenas/coffee-script/tarball/0.5.4">0.5.4</a>
<a href="http://github.com/jashkenas/coffee-script/tarball/0.6.2">0.6.2</a>
</p>
<h2>
@@ -125,7 +144,7 @@ alert reverse '!tpircseeffoC'</textarea></div>
<p>
For a longer CoffeeScript example, check out
<a href="documentation/underscore.html">Underscore.coffee</a>, a port
<a href="documentation/docs/underscore.html">Underscore.coffee</a>, a port
of the <a href="http://documentcloud.github.com/underscore/">Underscore.js</a>
library of helper functions. Underscore.coffee can pass the entire Underscore.js
test suite. The CoffeeScript version is faster than the original for a number
@@ -143,7 +162,7 @@ alert reverse '!tpircseeffoC'</textarea></div>
<p>
The CoffeeScript compiler is written in pure CoffeeScript, using a
<a href="http://github.com/jashkenas/coffee-script/blob/master/src/grammar.coffee">small DSL</a>
<a href="documentation/docs/grammar.html">small DSL</a>
on top of the <a href="http://github.com/zaach/jison">Jison parser generator</a>, and is available
as a <a href="http://nodejs.org/">Node.js</a> utility. The core compiler however,
does not depend on Node, and can be run in other server-side-JavaScript environments,
@@ -152,13 +171,12 @@ alert reverse '!tpircseeffoC'</textarea></div>
</p>
<p>
To install, first make sure you have a working version of
<a href="http://nodejs.org/">Node.js</a> greater than version 0.1.30 (Node
moves quickly, using the latest master is your best bet).
To install, first make sure you have a working copy of the latest tagged version of
<a href="http://nodejs.org/">Node.js</a>, currently <b>0.1.90</b> or higher.
Then clone the CoffeeScript
<a href="http://github.com/jashkenas/coffee-script">source repository</a>
from GitHub, or download the latest
release: <a href="http://github.com/jashkenas/coffee-script/tarball/0.5.4">0.5.4</a>.
release: <a href="http://github.com/jashkenas/coffee-script/tarball/0.6.2">0.6.2</a>.
To install the CoffeeScript compiler system-wide
under <tt>/usr/local</tt>, open the directory and run:
</p>
@@ -187,6 +205,8 @@ sudo bin/cake install</pre>
<td width="25%"><code>-i, --interactive</code></td>
<td>
Launch an interactive CoffeeScript session to try short snippets.
More pleasant if wrapped with
<a href="http://utopia.knoware.nl/~hlub/uck/rlwrap/rlwrap.html">rlwrap</a>.
</td>
</tr>
<tr>
@@ -225,14 +245,14 @@ sudo bin/cake install</pre>
<td>
Pipe in CoffeeScript to STDIN and get back JavaScript over STDOUT.
Good for use with processes written in other languages. An example:<br />
<tt>cat src/cake.coffee | coffee -s</tt>
<tt>cat src/cake.coffee | coffee -sc</tt>
</td>
</tr>
<tr>
<td><code>-e, --eval</code></td>
<td>
Compile and print a little snippet of CoffeeScript directly from the
command line. For example:<br /><tt>coffee -e "square: (x) -> x * x"</tt>
command line. For example:<br /><tt>coffee -e "puts num for num in [10..1]"</tt>
</td>
</tr>
<tr>
@@ -376,6 +396,13 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
should not be able to change the value of the external variable of the same name, and
therefore has a declaration of its own.
</p>
<p>
This behavior is effectively identical to Ruby's scope for local variables.
Because you don't have direct access to the <tt>var</tt> keyword,
it's impossible to shadow an outer variable on purpose, you may only refer
to it. So be careful that you're not reusing the name of an external
variable accidentally, if you're writing a deeply nested function.
</p>
<p>
Although suppressed within this documentation for clarity, all
CoffeeScript output is wrapped in an anonymous function:
@@ -405,10 +432,9 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('conditionals') %>
<p>
The conditional assignment operators are included: <tt>||=</tt>,
which only assigns a value to a variable if the variable's current value
is falsy, and <tt>&amp;&amp;=</tt>, which only replaces the value of
truthy variables.
You can assign a variable to a half-expression to perform an operation
like Ruby's <tt>||=</tt>, which only assigns a value to a variable
if the variable's current value is falsy.
</p>
<p>
@@ -455,19 +481,9 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('splats', true) %>
<p>
<span id="arguments" class="bookmark"></span>
<b class="header">Arguments are Arrays</b>
If you reference the <b>arguments object</b> directly, it will be converted
into a real Array, making all of the
<a href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array">Array methods</a>
available.
</p>
<%= code_for('arguments', true) %>
<p>
<span id="while" class="bookmark"></span>
<b class="header">While Loops</b>
<b class="header">While &amp; Until Loops</b>
The only low-level loop that CoffeeScript provides is the <b>while</b> loop. The
main difference from JavaScript is that the <b>while</b> loop can be used
as an expression, returning an array containing the result of each iteration
@@ -475,6 +491,7 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('while', 'lyrics.join("\n")') %>
<p>
For readability, the <b>until</b> loop serves as an inverted <b>while</b> loop.
Other JavaScript loops, such as <b>for</b> loops and <b>do-while</b> loops
can be mimicked by variations on <b>while</b>, but the hope is that you
won't need to do that with CoffeeScript, either because you're using
@@ -618,11 +635,15 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
<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
helps with proper prototype setup, as seen above, <tt>::</tt> gives you
helps with proper prototype setup, <tt>::</tt> gives you
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()') %>
<p>
Finally, you may assign Class-level (static) properties within a class
definition by using<br /><tt>@property: value</tt>
</p>
<p>
<span id="pattern_matching" class="bookmark"></span>
@@ -645,12 +666,24 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
Pattern matching can be used with any depth of array and object nesting,
to help pull out deeply nested properties.
</p>
<%= code_for('object_extraction', 'poet + " — " + street') %>
<%= code_for('object_extraction', 'name + " — " + street') %>
<p>
Pattern matching can even be combined with splats.
</p>
<%= code_for('patterns_and_splats', 'contents.join("")') %>
<p>
<span id="fat_arrow" class="bookmark"></span>
<b class="header">Function binding</b>
The fat arrow <tt>=></tt> can be used to both define a function, and to bind
In JavaScript, the <tt>this</tt> keyword is dynamically scoped to mean the
object that the current function is attached to. If you pass a function as
as callback, or attach it to a different object, the original value of <tt>this</tt>
will be lost. If you're not familiar with this behavior,
<a href="http://www.digital-web.com/articles/scope_in_javascript/">this Digital Web article</a>
gives a good overview of the quirks.
</p>
<p>
The fat arrow <tt>=&gt;</tt> can be used to both define a function, and to bind
it to the current value of <tt>this</tt>, right on the spot. This is helpful
when using callback-based libraries like Prototype or jQuery, for creating
iterator functions to pass to <tt>each</tt>, or event-handler functions
@@ -658,6 +691,20 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
properties of the <tt>this</tt> where they're defined.
</p>
<%= code_for('fat_arrow') %>
<p>
If we had used <tt>-></tt> in the callback above, <tt>@customer</tt> would
have referred to the undefined "customer" property of the DOM element,
and trying to call <tt>purchase()</tt> on it would have raised an exception.
</p>
<p>
If you have more custom needs for function binding, CoffeeScript includes
the <tt>&lt;-</tt> bind operator, which works the same as ECMAScript 5
and Prototype.js's <tt>Function#bind</tt>. The first argument is the <tt>this</tt>
value, and the remainder are curried arguments. In the example below,
we curry a jQuery request for a URL, and then execute the bound function
with the missing callback argument.
</p>
<%= code_for('binding', true) %>
<p>
<span id="embedded" class="bookmark"></span>
@@ -704,6 +751,20 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('comparisons', 'healthy') %>
<p>
<span id="interpolation" class="bookmark"></span>
<b class="header">String and RegExp Interpolation</b>
A version of <a href="http://wiki.ecmascript.org/doku.php?id=strawman:string_interpolation">ECMAScript Harmony's proposed string interpolation</a>
is included in CoffeeScript. Simple variables can be included by marking
them with a dollar sign.
</p>
<%= code_for('interpolation', 'quote') %>
<p>
And arbitrary expressions can be interpolated by using brackets <tt>${ ... }</tt><br />
Interpolation works the same way within regular expressions.
</p>
<%= code_for('interpolation_expression', 'sentence') %>
<p>
<span id="strings" class="bookmark"></span>
<b class="header">Multiline Strings and Heredocs</b>
@@ -717,6 +778,9 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
you can keep it all aligned with the body of your code.
</p>
<%= code_for('heredocs') %>
<p>
Double-quoted heredocs, like double-quoted strings, allow interpolation.
</p>
<h2>
<span id="cake" class="bookmark"></span>
@@ -779,7 +843,12 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
<tt>bin/cake test</tt> to run the test suite,<br />
<tt>bin/cake build</tt> to rebuild the CoffeeScript compiler, and <br />
<tt>bin/cake build:parser</tt> to regenerate the Jison parser if you're
working on the grammar.
working on the grammar. <br /><br />
<tt>git checkout lib &amp;&amp; bin/cake build:full</tt> is a good command to run when you're working
on the core language. It'll refresh the lib directory
(in case you broke something), build your altered compiler, use that to
rebuild itself (a good sanity test) and then run all of the tests. If
they pass, there's a good chance you've made a successful change.
</li>
<li>
<a href="http://github.com/jashkenas/coffee-script/issues">CoffeeScript Issues</a><br />
@@ -790,23 +859,129 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
IRC client of your choice, or on
<a href="http://webchat.freenode.net/">webchat.freenode.net</a>.
</li>
<li>
<b>yeungda</b>'s <a href="http://github.com/yeungda/jcoffeescript">JCoffeeScript</a>
&mdash; A Java Library that uses Rhino to compile CoffeeScript, allowing
compilation within Java projects or on systems that Node.js doesn't support.
</li>
<li>
<b>defunkt</b>'s <a href="http://github.com/defunkt/coffee-mode">CoffeeScript Major Mode</a>
&mdash; a Emacs major mode that provides syntax highlighting, indentation
support, and some bonus commands.
</li>
<li>
<b>jashkenas</b>'s <a href="http://github.com/jashkenas/coffee-script-tmbundle">CoffeeScript TextMate Bundle</a>
&mdash; which provides syntax highlighting, snippet expansion, and the
ability to run bits of CoffeeScript from within TextMate itself.
</li>
<li>
<b>kchmck</b>'s <a href="http://github.com/kchmck/vim-coffee-script">Vim CoffeeScript</a>
&mdash; which adds Vim syntax highlighting and indentation support.
</li>
<li>
<b>yeungda</b>'s <a href="http://yeungda.github.com/coffeescript-idea/">coffeescript-idea</a>
&mdash; a plugin for IntelliJ IDEA and RubyMine providing syntax highlighting.
</li>
<li>
<b>mattly</b>'s <a href="http://github.com/mattly/rack-coffee">rack-coffee</a>
&mdash; a small Rack middleware for serving CoffeeScript files as
compiled JavaScript on the fly.
</li>
<li>
<b>jnicklas</b>'s <a href="http://github.com/jnicklas/bistro_car">BistroCar</a>
&mdash; a plugin that serves and bundles CoffeeScript from within your
Rails application.
</li>
<li>
<b>sutto</b>'s <a href="http://github.com/Sutto/barista">Barista</a>
&mdash; a BistroCar alternative that integrates well with
<a href="http://documentcloud.github.com/jammit">Jammit</a> and Rails 3.
</li>
<li>
<b>inem</b> and <b>gerad</b>'s <a href="http://github.com/gerad/coffee-haml-filter">coffee-haml-filter</a>
&mdash; a custom filter for rendering CoffeeScript inline within
<a href="http://haml-lang.com/">HAML</a> templates.
</li>
</ul>
<h2>
<span id="webchat" class="bookmark"></span>
Web Chat (IRC)
</h2>
<p>
Quick help and advice can usually be found in the CoffeeScript IRC room.
Join <tt>#coffeescript</tt> on <tt>irc.freenode.net</tt>, or click the
button below to open a webchat session on this page.
</p>
<p>
<button id="open_webchat">click to open #coffeescript</button>
</p>
<h2>
<span id="change_log" class="bookmark"></span>
Change Log
</h2>
<p>
<b class="header" style="margin-top: 20px;">0.6.2</b>
The <tt>coffee</tt> command will now preserve directory structure when
compiling a directory full of scripts. Fixed two omissions that were preventing
the CoffeeScript compiler from running live within Internet Explorer.
There's now a syntax for block comments, similar in spirit to CoffeeScript's heredocs.
ECMA Harmony DRY-style pattern matching is now supported, where the name
of the property is the same as the name of the value: <tt>{name, length}: func</tt>.
Pattern matching is now allowed within comprehension variables. <tt>unless</tt>
is now allowed in block form. <tt>until</tt> loops were added, as the inverse
of <tt>while</tt> loops. <tt>switch</tt> statements are now allowed without
switch object clauses. Compatible
with Node.js <b>v0.1.95</b>.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.6.1</b>
Upgraded CoffeeScript for compatibility with the new Node.js <b>v0.1.90</b>
series.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.6.0</b>
Trailing commas are now allowed, a-la Python. Static
properties may be assigned directly within class definitions,
using <tt>@property</tt> notation.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.5.6</b>
Interpolation can now be used within regular expressions and heredocs, as well as
strings. Added the <tt>&lt;-</tt> bind operator.
Allowing assignment to half-expressions instead of special <tt>||=</tt>-style
operators. The arguments object is no longer automatically converted into
an array. After requiring <tt>coffee-script</tt>, Node.js can now directly
load <tt>.coffee</tt> files, thanks to <b>registerExtension</b>. Multiple
splats can now be used in function calls, arrays, and pattern matching.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.5.5</b>
String interpolation, contributed by
<a href="http://github.com/StanAngeloff">Stan Angeloff</a>.
Since <tt>--run</tt> has been the default since <b>0.5.3</b>, updating
<tt>--stdio</tt> and <tt>--eval</tt> to run by default, pass <tt>--compile</tt>
as well if you'd like to print the result.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.5.4</b>
Bugfix that corrects the Node.js global constants <tt>__filename</tt> and
<tt>__dirname</tt>. Tweaks for more flexible parsing of nested function
literals and improperly-indented comments. Updates for the latest Node.js API.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.5.3</b>
CoffeeScript now has a syntax for defining classes. Many of the core
CoffeeScript now has a syntax for defining classes. Many of the core
components (Nodes, Lexer, Rewriter, Scope, Optparse) are using them.
Cakefiles can use <tt>optparse.coffee</tt> to define options for tasks.
<tt>--run</tt> is now the default flag for the <tt>coffee</tt> command,
@@ -1041,6 +1216,9 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
$('.navigation .minimize').click ->
document.body.className: 'minimized'
$('#open_webchat').click ->
$(this).replaceWith $('<iframe src="http://webchat.freenode.net/?channels=coffeescript" width="625" height="400"></iframe>')
compile_source()
</script>

View File

@@ -1,8 +0,0 @@
(function(){
var backwards;
backwards = function backwards() {
arguments = Array.prototype.slice.call(arguments, 0);
return alert(arguments.reverse());
};
backwards("stairway", "to", "heaven");
})();

View File

@@ -2,20 +2,20 @@
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, food, lunch, roid, roid2;
// Eat lunch.
lunch = (function() {
_a = []; _b = ['toast', 'cheese', 'wine'];
for (_c = 0, _d = _b.length; _c < _d; _c++) {
food = _b[_c];
_a = []; _c = ['toast', 'cheese', 'wine'];
for (_b = 0, _d = _c.length; _b < _d; _b++) {
food = _c[_b];
_a.push(eat(food));
}
return _a;
}).call(this);
})();
// Naive collision detection.
_e = asteroids;
for (_f = 0, _g = _e.length; _f < _g; _f++) {
roid = _e[_f];
_h = asteroids;
for (_i = 0, _j = _h.length; _i < _j; _i++) {
roid2 = _h[_i];
_f = asteroids;
for (_e = 0, _g = _f.length; _e < _g; _e++) {
roid = _f[_e];
_i = asteroids;
for (_h = 0, _j = _i.length; _h < _j; _h++) {
roid2 = _i[_h];
if (roid !== roid2) {
if (roid.overlaps(roid2)) {
roid.explode();

View File

@@ -0,0 +1,13 @@
(function(){
var get_source, url;
var __slice = Array.prototype.slice, __bind = function(func, obj, args) {
return function() {
return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);
};
};
url = "documentation/coffee/binding.coffee";
get_source = __bind(jQuery.get, jQuery, [url]);
get_source(function(response) {
return alert(response);
});
})();

View File

@@ -1,10 +1,9 @@
(function(){
process.mixin(require('assert'));
task('test', 'run each of the unit tests', function() {
var _a, _b, _c, _d, test;
_a = []; _b = test_files;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
test = _b[_c];
_a = []; _c = test_files;
for (_b = 0, _d = _c.length; _b < _d; _b++) {
test = _c[_b];
_a.push(fs.readFile(test, function(err, code) {
return eval(coffee.compile(code));
}));

View File

@@ -7,28 +7,31 @@
child.prototype = new ctor();
child.prototype.constructor = child;
};
Animal = function Animal() { };
Animal.prototype.move = function move(meters) {
Animal = function() { };
Animal.prototype.move = function(meters) {
return alert(this.name + " moved " + meters + "m.");
};
Snake = function Snake(name) {
Snake = function(name) {
this.name = name;
return this;
};
__extends(Snake, Animal);
Snake.prototype.move = function move() {
Snake.prototype.move = function() {
alert("Slithering...");
return Snake.__superClass__.move.call(this, 5);
};
Horse = function Horse(name) {
Horse = function(name) {
this.name = name;
return this;
};
__extends(Horse, Animal);
Horse.prototype.move = function move() {
Horse.prototype.move = function() {
alert("Galloping...");
return Horse.__superClass__.move.call(this, 45);
};
sam = new Snake("Sammy the Python");
tom = new Horse("Tommy the Palomino");
sam.move();

View File

@@ -1,10 +1,14 @@
(function(){
var eldest, grade;
grade = function grade(student) {
grade = function(student) {
if (student.excellent_work) {
return "A+";
} else if (student.okay_stuff) {
return student.tried_hard ? "B" : "B-";
if (student.tried_hard) {
return "B";
} else {
return "B-";
}
} else {
return "C";
}

View File

@@ -1,4 +1,4 @@
(function(){
var one, six, three, two;
six = ((one = 1)) + ((two = 2)) + ((three = 3));
six = (one = 1) + (two = 2) + (three = 3);
})();

View File

@@ -8,5 +8,5 @@
_a.push(name);
}}
return _a;
}).call(this).slice(0, 10);
})().slice(0, 10);
})();

View File

@@ -5,5 +5,5 @@
} catch (error) {
return "And the error is ... " + error;
}
}).call(this));
})());
})();

View File

@@ -1,15 +1,15 @@
(function(){
var Account;
Account = function Account(customer, cart) {
var __slice = Array.prototype.slice, __bind = function(func, obj, args) {
return function() {
return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);
};
};
Account = function(customer, cart) {
this.customer = customer;
this.cart = cart;
return $('.shopping_cart').bind('click', (function(__this) {
var __func = function(event) {
return $('.shopping_cart').bind('click', __bind(function(event) {
return this.customer.purchase(this.cart);
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
}, this));
};
})();

View File

@@ -1,9 +1,9 @@
(function(){
var cube, square;
square = function square(x) {
square = function(x) {
return x * x;
};
cube = function cube(x) {
cube = function(x) {
return square(x) * x;
};
})();

View File

@@ -1,4 +1,4 @@
(function(){
var html;
html = "<strong>\n cup of coffeescript\n</strong>";
html = '<strong>\n cup of coffeescript\n</strong>';
})();

View File

@@ -0,0 +1,5 @@
(function(){
var author, quote;
author = "Wittgenstein";
quote = ("A picture is a fact. -- " + author);
})();

View File

@@ -0,0 +1,6 @@
(function(){
var dates, sentence, sep;
sentence = ("" + (22 / 7) + " is a decent approximation of π");
sep = "[.\\/\\- ]";
dates = (new RegExp(("\\d+" + sep + "\\d+" + sep + "\\d+"), "g"));
})();

View File

@@ -1,6 +1,6 @@
(function(){
var _a, city, forecast, temp, weather_report;
weather_report = function weather_report(location) {
weather_report = function(location) {
// Make an Ajax request to fetch the weather...
return [location, 72, "Mostly Sunny"];
};

View File

@@ -13,5 +13,5 @@
_a.push(child + " is " + age);
}}
return _a;
}).call(this);
})();
})();

View File

@@ -1,5 +1,5 @@
(function(){
var _a, _b, _c, city, futurists, poet, street;
var _a, _b, _c, city, futurists, name, street;
futurists = {
sculptor: "Umberto Boccioni",
painter: "Vladimir Burliuk",
@@ -10,7 +10,7 @@
};
_a = futurists;
_b = _a.poet;
poet = _b.name;
name = _b.name;
_c = _b.address;
street = _c[0];
city = _c[1];

View File

@@ -1,5 +1,6 @@
(function(){
var _a, _b, _c, _d, cubed_list, list, math, num, number, opposite_day, race, square;
var __slice = Array.prototype.slice;
// Assignment:
number = 42;
opposite_day = true;
@@ -8,7 +9,7 @@
number = -42;
}
// Functions:
square = function square(x) {
square = function(x) {
return x * x;
};
// Arrays:
@@ -17,14 +18,15 @@
math = {
root: Math.sqrt,
square: square,
cube: function cube(x) {
cube: function(x) {
return x * square(x);
}
};
// Splats:
race = function race(winner) {
race = function(winner) {
var runners;
runners = Array.prototype.slice.call(arguments, 1);
var _a = arguments.length, _b = _a >= 2;
runners = __slice.call(arguments, 1, _a - 0);
return print(winner, runners);
};
// Existence:
@@ -33,11 +35,11 @@
}
// Array comprehensions:
cubed_list = (function() {
_a = []; _b = list;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
num = _b[_c];
_a = []; _c = list;
for (_b = 0, _d = _c.length; _b < _d; _b++) {
num = _c[_b];
_a.push(math.cube(num));
}
return _a;
}).call(this);
})();
})();

View File

@@ -0,0 +1,9 @@
(function(){
var _a, close, contents, open, tag;
var __slice = Array.prototype.slice;
tag = "<impossible>";
_a = tag.split("");
open = _a[0];
contents = __slice.call(_a, 1, _a.length - 1);
close = _a[_a.length - 1];
})();

View File

@@ -1,5 +1,5 @@
(function(){
String.prototype.dasherize = function dasherize() {
String.prototype.dasherize = function() {
return this.replace(/_/g, "-");
};
})();

View File

@@ -1,21 +1,21 @@
(function(){
var _a, _b, _c, _d, _e, countdown, egg_delivery, num;
var _a, _b, _c, countdown, egg_delivery, num;
countdown = (function() {
_a = []; _d = 10; _e = 1;
for (_c = 0, num = _d; (_d <= _e ? num <= _e : num >= _e); (_d <= _e ? num += 1 : num -= 1), _c++) {
_a = []; _b = 10; _c = 1;
for (num = _b; (_b <= _c ? num <= _c : num >= _c); (_b <= _c ? num += 1 : num -= 1)) {
_a.push(num);
}
return _a;
}).call(this);
egg_delivery = function egg_delivery() {
var _f, _g, _h, _i, _j, dozen_eggs, i;
_f = []; _i = 0; _j = eggs.length;
for (_h = 0, i = _i; (_i <= _j ? i < _j : i > _j); (_i <= _j ? i += 12 : i -= 12), _h++) {
_f.push((function() {
})();
egg_delivery = function() {
var _d, _e, _f, dozen_eggs, i;
_d = []; _e = 0; _f = eggs.length;
for (i = _e; (_e <= _f ? i < _f : i > _f); (_e <= _f ? i += 12 : i -= 12)) {
_d.push((function() {
dozen_eggs = eggs.slice(i, i + 12);
return deliver(new egg_carton(dozen));
}).call(this));
})());
}
return _f;
return _d;
};
})();

View File

@@ -1,10 +1,11 @@
(function(){
var change_numbers, new_num, num;
num = 1;
change_numbers = function change_numbers() {
change_numbers = function() {
var new_num;
new_num = -1;
return num = 10;
num = 10;
return num;
};
new_num = change_numbers();
})();

View File

@@ -1,4 +1,4 @@
(function(){
var _a;
(_a = lottery.draw_winner()) == undefined ? undefined : _a.address == undefined ? undefined : _a.address.zipcode;
typeof (_a = (lottery.draw_winner())) === "undefined" || _a == undefined ? undefined : _a.address == undefined ? undefined : _a.address.zipcode;
})();

View File

@@ -1,12 +1,15 @@
(function(){
var award_medals, contenders, gold, silver, the_field;
var __slice = Array.prototype.slice;
gold = (silver = (the_field = "unknown"));
award_medals = function award_medals(first, second) {
award_medals = function(first, second) {
var rest;
rest = Array.prototype.slice.call(arguments, 2);
var _a = arguments.length, _b = _a >= 3;
rest = __slice.call(arguments, 2, _a - 0);
gold = first;
silver = second;
return the_field = rest;
the_field = rest;
return the_field;
};
contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];
award_medals.apply(this, contenders);

View File

@@ -5,7 +5,7 @@
while (supply > demand) {
buy();
}
while (supply < demand) {
while (!(supply > demand)) {
sell();
}
}
@@ -18,5 +18,5 @@
One fell out and bumped his head.");
}
return _a;
}).call(this);
})();
})();

View File

@@ -1,668 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<link rel="stylesheet" type="text/css" media="screen,projection,print" href="css/idle.css" />
<title>Underscore.coffee</title>
</head>
<body>
<pre class="idle"><span class="line-numbers"> 1 </span>
<span class="line-numbers"> 2 </span> <span class="Comment"><span class="Comment">#</span> Underscore.coffee</span>
<span class="line-numbers"> 3 </span> <span class="Comment"><span class="Comment">#</span> (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.</span>
<span class="line-numbers"> 4 </span> <span class="Comment"><span class="Comment">#</span> Underscore is freely distributable under the terms of the MIT license.</span>
<span class="line-numbers"> 5 </span> <span class="Comment"><span class="Comment">#</span> Portions of Underscore are inspired by or borrowed from Prototype.js,</span>
<span class="line-numbers"> 6 </span> <span class="Comment"><span class="Comment">#</span> Oliver Steele's Functional, and John Resig's Micro-Templating.</span>
<span class="line-numbers"> 7 </span> <span class="Comment"><span class="Comment">#</span> For all details and documentation:</span>
<span class="line-numbers"> 8 </span> <span class="Comment"><span class="Comment">#</span> http://documentcloud.github.com/underscore/</span>
<span class="line-numbers"> 9 </span>
<span class="line-numbers"> 10 </span>
<span class="line-numbers"> 11 </span> <span class="Comment"><span class="Comment">#</span> ------------------------- Baseline setup ---------------------------------</span>
<span class="line-numbers"> 12 </span>
<span class="line-numbers"> 13 </span> <span class="Comment"><span class="Comment">#</span> Establish the root object, &quot;window&quot; in the browser, or &quot;global&quot; on the server.</span>
<span class="line-numbers"> 14 </span> <span class="FunctionName">root</span><span class="Keyword">:</span> <span class="Variable">this</span>
<span class="line-numbers"> 15 </span>
<span class="line-numbers"> 16 </span>
<span class="line-numbers"> 17 </span> <span class="Comment"><span class="Comment">#</span> Save the previous value of the &quot;_&quot; variable.</span>
<span class="line-numbers"> 18 </span> <span class="FunctionName">previousUnderscore</span><span class="Keyword">:</span> root._
<span class="line-numbers"> 19 </span>
<span class="line-numbers"> 20 </span>
<span class="line-numbers"> 21 </span> <span class="Comment"><span class="Comment">#</span> Establish the object that gets thrown to break out of a loop iteration.</span>
<span class="line-numbers"> 22 </span> <span class="FunctionName">breaker</span><span class="Keyword">:</span> <span class="Keyword">if</span> <span class="Keyword">typeof</span>(StopIteration) <span class="Keyword">is</span> <span class="String"><span class="String">'</span>undefined<span class="String">'</span></span> <span class="Keyword">then</span> <span class="String"><span class="String">'</span>__break__<span class="String">'</span></span> <span class="Keyword">else</span> StopIteration
<span class="line-numbers"> 23 </span>
<span class="line-numbers"> 24 </span>
<span class="line-numbers"> 25 </span> <span class="Comment"><span class="Comment">#</span> Quick regexp-escaping function, because JS doesn't have RegExp.escape().</span>
<span class="line-numbers"> 26 </span> <span class="FunctionName">escapeRegExp</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">string</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> string.replace(<span class="String"><span class="String">/</span>([.*+?^${}()|[<span class="UserDefinedConstant">\]</span><span class="UserDefinedConstant">\/</span><span class="UserDefinedConstant">\\</span>])<span class="String">/</span>g</span>, <span class="String"><span class="String">'</span><span class="UserDefinedConstant">\\</span>$1<span class="String">'</span></span>)
<span class="line-numbers"> 27 </span>
<span class="line-numbers"> 28 </span>
<span class="line-numbers"> 29 </span> <span class="Comment"><span class="Comment">#</span> Save bytes in the minified (but not gzipped) version:</span>
<span class="line-numbers"> 30 </span> <span class="FunctionName">ArrayProto</span><span class="Keyword">:</span> Array.prototype
<span class="line-numbers"> 31 </span> <span class="FunctionName">ObjProto</span><span class="Keyword">:</span> Object.prototype
<span class="line-numbers"> 32 </span>
<span class="line-numbers"> 33 </span>
<span class="line-numbers"> 34 </span> <span class="Comment"><span class="Comment">#</span>Create quick reference variables for speed access to core prototypes.</span>
<span class="line-numbers"> 35 </span> <span class="FunctionName">slice</span><span class="Keyword">:</span> ArrayProto.slice
<span class="line-numbers"> 36 </span> <span class="FunctionName">unshift</span><span class="Keyword">:</span> ArrayProto.unshift
<span class="line-numbers"> 37 </span> <span class="FunctionName">toString</span><span class="Keyword">:</span> ObjProto.toString
<span class="line-numbers"> 38 </span> <span class="FunctionName">hasOwnProperty</span><span class="Keyword">:</span> ObjProto.hasOwnProperty
<span class="line-numbers"> 39 </span> <span class="FunctionName">propertyIsEnumerable</span><span class="Keyword">:</span> ObjProto.propertyIsEnumerable
<span class="line-numbers"> 40 </span>
<span class="line-numbers"> 41 </span>
<span class="line-numbers"> 42 </span> <span class="Comment"><span class="Comment">#</span> All ECMA5 native implementations we hope to use are declared here.</span>
<span class="line-numbers"> 43 </span> <span class="FunctionName">nativeForEach</span><span class="Keyword">:</span> ArrayProto.forEach
<span class="line-numbers"> 44 </span> <span class="FunctionName">nativeMap</span><span class="Keyword">:</span> ArrayProto.map
<span class="line-numbers"> 45 </span> <span class="FunctionName">nativeReduce</span><span class="Keyword">:</span> ArrayProto.reduce
<span class="line-numbers"> 46 </span> <span class="FunctionName">nativeReduceRight</span><span class="Keyword">:</span> ArrayProto.reduceRight
<span class="line-numbers"> 47 </span> <span class="FunctionName">nativeFilter</span><span class="Keyword">:</span> ArrayProto.filter
<span class="line-numbers"> 48 </span> <span class="FunctionName">nativeEvery</span><span class="Keyword">:</span> ArrayProto.every
<span class="line-numbers"> 49 </span> <span class="FunctionName">nativeSome</span><span class="Keyword">:</span> ArrayProto.some
<span class="line-numbers"> 50 </span> <span class="FunctionName">nativeIndexOf</span><span class="Keyword">:</span> ArrayProto.indexOf
<span class="line-numbers"> 51 </span> <span class="FunctionName">nativeLastIndexOf</span><span class="Keyword">:</span> ArrayProto.lastIndexOf
<span class="line-numbers"> 52 </span> <span class="FunctionName">nativeIsArray</span><span class="Keyword">:</span> Array.isArray
<span class="line-numbers"> 53 </span> <span class="FunctionName">nativeKeys</span><span class="Keyword">:</span> Object.keys
<span class="line-numbers"> 54 </span>
<span class="line-numbers"> 55 </span>
<span class="line-numbers"> 56 </span> <span class="Comment"><span class="Comment">#</span> Create a safe reference to the Underscore object for use below.</span>
<span class="line-numbers"> 57 </span> <span class="FunctionName">_</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="Keyword">new</span> <span class="TypeName">wrapper</span>(obj)
<span class="line-numbers"> 58 </span>
<span class="line-numbers"> 59 </span>
<span class="line-numbers"> 60 </span> <span class="Comment"><span class="Comment">#</span> Export the Underscore object for CommonJS.</span>
<span class="line-numbers"> 61 </span> <span class="Keyword">if</span> <span class="Keyword">typeof</span>(exports) <span class="Keyword">!</span><span class="Keyword">=</span> <span class="String"><span class="String">'</span>undefined<span class="String">'</span></span> <span class="Keyword">then</span> <span class="FunctionName">exports._</span><span class="Keyword">:</span> _
<span class="line-numbers"> 62 </span>
<span class="line-numbers"> 63 </span>
<span class="line-numbers"> 64 </span> <span class="Comment"><span class="Comment">#</span> Export Underscore to global scope.</span>
<span class="line-numbers"> 65 </span> <span class="FunctionName">root._</span><span class="Keyword">:</span> _
<span class="line-numbers"> 66 </span>
<span class="line-numbers"> 67 </span>
<span class="line-numbers"> 68 </span> <span class="Comment"><span class="Comment">#</span> Current version.</span>
<span class="line-numbers"> 69 </span> <span class="FunctionName">_.VERSION</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span>0.6.0<span class="String">'</span></span>
<span class="line-numbers"> 70 </span>
<span class="line-numbers"> 71 </span>
<span class="line-numbers"> 72 </span> <span class="Comment"><span class="Comment">#</span> ------------------------ Collection Functions: ---------------------------</span>
<span class="line-numbers"> 73 </span>
<span class="line-numbers"> 74 </span> <span class="Comment"><span class="Comment">#</span> The cornerstone, an each implementation.</span>
<span class="line-numbers"> 75 </span> <span class="Comment"><span class="Comment">#</span> Handles objects implementing forEach, arrays, and raw objects.</span>
<span class="line-numbers"> 76 </span> <span class="FunctionName">_.each</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 77 </span> <span class="Keyword">try</span>
<span class="line-numbers"> 78 </span> <span class="Keyword">if</span> nativeForEach <span class="Keyword">and</span> obj.forEach <span class="Keyword">is</span> nativeForEach
<span class="line-numbers"> 79 </span> obj.forEach iterator, context
<span class="line-numbers"> 80 </span> <span class="Keyword">else</span> <span class="Keyword">if</span> _.isNumber obj.length
<span class="line-numbers"> 81 </span> iterator.call(context, obj[i], i, obj) <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...obj.length]
<span class="line-numbers"> 82 </span> <span class="Keyword">else</span>
<span class="line-numbers"> 83 </span> iterator.call(context, val, key, obj) <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
<span class="line-numbers"> 84 </span> <span class="Keyword">catch</span> e
<span class="line-numbers"> 85 </span> <span class="Keyword">throw</span> e <span class="Keyword">if</span> e <span class="Keyword">isnt</span> breaker
<span class="line-numbers"> 86 </span> obj
<span class="line-numbers"> 87 </span>
<span class="line-numbers"> 88 </span>
<span class="line-numbers"> 89 </span> <span class="Comment"><span class="Comment">#</span> Return the results of applying the iterator to each element. Use JavaScript</span>
<span class="line-numbers"> 90 </span> <span class="Comment"><span class="Comment">#</span> 1.6's version of map, if possible.</span>
<span class="line-numbers"> 91 </span> <span class="FunctionName">_.map</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 92 </span> <span class="Keyword">return</span> obj.map(iterator, context) <span class="Keyword">if</span> nativeMap <span class="Keyword">and</span> obj.map <span class="Keyword">is</span> nativeMap
<span class="line-numbers"> 93 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
<span class="line-numbers"> 94 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 95 </span> results.push iterator.call context, value, index, list
<span class="line-numbers"> 96 </span> results
<span class="line-numbers"> 97 </span>
<span class="line-numbers"> 98 </span>
<span class="line-numbers"> 99 </span> <span class="Comment"><span class="Comment">#</span> Reduce builds up a single result from a list of values. Also known as</span>
<span class="line-numbers"> 100 </span> <span class="Comment"><span class="Comment">#</span> inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.</span>
<span class="line-numbers"> 101 </span> <span class="FunctionName">_.reduce</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, memo, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 102 </span> <span class="Keyword">return</span> obj.reduce(_.bind(iterator, context), memo) <span class="Keyword">if</span> nativeReduce <span class="Keyword">and</span> obj.reduce <span class="Keyword">is</span> nativeReduce
<span class="line-numbers"> 103 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 104 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> iterator.call context, memo, value, index, list
<span class="line-numbers"> 105 </span> memo
<span class="line-numbers"> 106 </span>
<span class="line-numbers"> 107 </span>
<span class="line-numbers"> 108 </span> <span class="Comment"><span class="Comment">#</span> The right-associative version of reduce, also known as foldr. Uses</span>
<span class="line-numbers"> 109 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.8's version of reduceRight, if available.</span>
<span class="line-numbers"> 110 </span> <span class="FunctionName">_.reduceRight</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, memo, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 111 </span> <span class="Keyword">return</span> obj.reduceRight(_.bind(iterator, context), memo) <span class="Keyword">if</span> nativeReduceRight <span class="Keyword">and</span> obj.reduceRight <span class="Keyword">is</span> nativeReduceRight
<span class="line-numbers"> 112 </span> _.each _.clone(_.toArray(obj)).reverse(), <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 113 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> iterator.call context, memo, value, index, obj
<span class="line-numbers"> 114 </span> memo
<span class="line-numbers"> 115 </span>
<span class="line-numbers"> 116 </span>
<span class="line-numbers"> 117 </span> <span class="Comment"><span class="Comment">#</span> Return the first value which passes a truth test.</span>
<span class="line-numbers"> 118 </span> <span class="FunctionName">_.detect</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 119 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">null</span>
<span class="line-numbers"> 120 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 121 </span> <span class="Keyword">if</span> iterator.call context, value, index, list
<span class="line-numbers"> 122 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> value
<span class="line-numbers"> 123 </span> _.breakLoop()
<span class="line-numbers"> 124 </span> result
<span class="line-numbers"> 125 </span>
<span class="line-numbers"> 126 </span>
<span class="line-numbers"> 127 </span> <span class="Comment"><span class="Comment">#</span> Return all the elements that pass a truth test. Use JavaScript 1.6's</span>
<span class="line-numbers"> 128 </span> <span class="Comment"><span class="Comment">#</span> filter(), if it exists.</span>
<span class="line-numbers"> 129 </span> <span class="FunctionName">_.filter</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 130 </span> <span class="Keyword">return</span> obj.filter iterator, context <span class="Keyword">if</span> nativeFilter <span class="Keyword">and</span> obj.filter <span class="Keyword">is</span> nativeFilter
<span class="line-numbers"> 131 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
<span class="line-numbers"> 132 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 133 </span> results.push value <span class="Keyword">if</span> iterator.call context, value, index, list
<span class="line-numbers"> 134 </span> results
<span class="line-numbers"> 135 </span>
<span class="line-numbers"> 136 </span>
<span class="line-numbers"> 137 </span> <span class="Comment"><span class="Comment">#</span> Return all the elements for which a truth test fails.</span>
<span class="line-numbers"> 138 </span> <span class="FunctionName">_.reject</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 139 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
<span class="line-numbers"> 140 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 141 </span> results.push value <span class="Keyword">if</span> <span class="Keyword">not</span> iterator.call context, value, index, list
<span class="line-numbers"> 142 </span> results
<span class="line-numbers"> 143 </span>
<span class="line-numbers"> 144 </span>
<span class="line-numbers"> 145 </span> <span class="Comment"><span class="Comment">#</span> Determine whether all of the elements match a truth test. Delegate to</span>
<span class="line-numbers"> 146 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.6's every(), if it is present.</span>
<span class="line-numbers"> 147 </span> <span class="FunctionName">_.every</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 148 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
<span class="line-numbers"> 149 </span> <span class="Keyword">return</span> obj.every iterator, context <span class="Keyword">if</span> nativeEvery <span class="Keyword">and</span> obj.every <span class="Keyword">is</span> nativeEvery
<span class="line-numbers"> 150 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 151 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 152 </span> _.breakLoop() <span class="Keyword">unless</span> (<span class="FunctionName">result</span><span class="Keyword">:</span> result <span class="Keyword">and</span> iterator.call(context, value, index, list))
<span class="line-numbers"> 153 </span> result
<span class="line-numbers"> 154 </span>
<span class="line-numbers"> 155 </span>
<span class="line-numbers"> 156 </span> <span class="Comment"><span class="Comment">#</span> Determine if at least one element in the object matches a truth test. Use</span>
<span class="line-numbers"> 157 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.6's some(), if it exists.</span>
<span class="line-numbers"> 158 </span> <span class="FunctionName">_.some</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 159 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
<span class="line-numbers"> 160 </span> <span class="Keyword">return</span> obj.some iterator, context <span class="Keyword">if</span> nativeSome <span class="Keyword">and</span> obj.some <span class="Keyword">is</span> nativeSome
<span class="line-numbers"> 161 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">false</span>
<span class="line-numbers"> 162 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 163 </span> _.breakLoop() <span class="Keyword">if</span> (<span class="FunctionName">result</span><span class="Keyword">:</span> iterator.call(context, value, index, list))
<span class="line-numbers"> 164 </span> result
<span class="line-numbers"> 165 </span>
<span class="line-numbers"> 166 </span>
<span class="line-numbers"> 167 </span> <span class="Comment"><span class="Comment">#</span> Determine if a given value is included in the array or object,</span>
<span class="line-numbers"> 168 </span> <span class="Comment"><span class="Comment">#</span> based on '==='.</span>
<span class="line-numbers"> 169 </span> <span class="FunctionName">_.include</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, target</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 170 </span> <span class="Keyword">return</span> _.indexOf(obj, target) <span class="Keyword">isnt</span> <span class="Keyword">-</span><span class="Number">1</span> <span class="Keyword">if</span> nativeIndexOf <span class="Keyword">and</span> obj.indexOf <span class="Keyword">is</span> nativeIndexOf
<span class="line-numbers"> 171 </span> <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
<span class="line-numbers"> 172 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> val <span class="Keyword">is</span> target
<span class="line-numbers"> 173 </span> <span class="BuiltInConstant">false</span>
<span class="line-numbers"> 174 </span>
<span class="line-numbers"> 175 </span>
<span class="line-numbers"> 176 </span> <span class="Comment"><span class="Comment">#</span> Invoke a method with arguments on every item in a collection.</span>
<span class="line-numbers"> 177 </span> <span class="FunctionName">_.invoke</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, method</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 178 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest arguments, <span class="Number">2</span>
<span class="line-numbers"> 179 </span> (<span class="Keyword">if</span> method <span class="Keyword">then</span> val[method] <span class="Keyword">else</span> val).apply(val, args) <span class="Keyword">for</span> val <span class="Keyword">in</span> obj
<span class="line-numbers"> 180 </span>
<span class="line-numbers"> 181 </span>
<span class="line-numbers"> 182 </span> <span class="Comment"><span class="Comment">#</span> Convenience version of a common use case of map: fetching a property.</span>
<span class="line-numbers"> 183 </span> <span class="FunctionName">_.pluck</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, key</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 184 </span> _.map(obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">val</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> val[key])
<span class="line-numbers"> 185 </span>
<span class="line-numbers"> 186 </span>
<span class="line-numbers"> 187 </span> <span class="Comment"><span class="Comment">#</span> Return the maximum item or (item-based computation).</span>
<span class="line-numbers"> 188 </span> <span class="FunctionName">_.max</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 189 </span> <span class="Keyword">return</span> Math.max.apply(Math, obj) <span class="Keyword">if</span> <span class="Keyword">not</span> iterator <span class="Keyword">and</span> _.isArray(obj)
<span class="line-numbers"> 190 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> {<span class="FunctionName">computed</span><span class="Keyword">:</span> <span class="Keyword">-</span><span class="BuiltInConstant">Infinity</span>}
<span class="line-numbers"> 191 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 192 </span> <span class="FunctionName">computed</span><span class="Keyword">:</span> <span class="Keyword">if</span> iterator <span class="Keyword">then</span> iterator.call(context, value, index, list) <span class="Keyword">else</span> value
<span class="line-numbers"> 193 </span> computed <span class="Keyword">&gt;=</span> result.computed <span class="Keyword">and</span> (<span class="FunctionName">result</span><span class="Keyword">:</span> {<span class="FunctionName">value</span><span class="Keyword">:</span> value, <span class="FunctionName">computed</span><span class="Keyword">:</span> computed})
<span class="line-numbers"> 194 </span> result.value
<span class="line-numbers"> 195 </span>
<span class="line-numbers"> 196 </span>
<span class="line-numbers"> 197 </span> <span class="Comment"><span class="Comment">#</span> Return the minimum element (or element-based computation).</span>
<span class="line-numbers"> 198 </span> <span class="FunctionName">_.min</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 199 </span> <span class="Keyword">return</span> Math.min.apply(Math, obj) <span class="Keyword">if</span> <span class="Keyword">not</span> iterator <span class="Keyword">and</span> _.isArray(obj)
<span class="line-numbers"> 200 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> {<span class="FunctionName">computed</span><span class="Keyword">:</span> <span class="BuiltInConstant">Infinity</span>}
<span class="line-numbers"> 201 </span> _.each obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 202 </span> <span class="FunctionName">computed</span><span class="Keyword">:</span> <span class="Keyword">if</span> iterator <span class="Keyword">then</span> iterator.call(context, value, index, list) <span class="Keyword">else</span> value
<span class="line-numbers"> 203 </span> computed <span class="Keyword">&lt;</span> result.computed <span class="Keyword">and</span> (<span class="FunctionName">result</span><span class="Keyword">:</span> {<span class="FunctionName">value</span><span class="Keyword">:</span> value, <span class="FunctionName">computed</span><span class="Keyword">:</span> computed})
<span class="line-numbers"> 204 </span> result.value
<span class="line-numbers"> 205 </span>
<span class="line-numbers"> 206 </span>
<span class="line-numbers"> 207 </span> <span class="Comment"><span class="Comment">#</span> Sort the object's values by a criterion produced by an iterator.</span>
<span class="line-numbers"> 208 </span> <span class="FunctionName">_.sortBy</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 209 </span> _.pluck(((_.map obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 210 </span> {<span class="FunctionName">value</span><span class="Keyword">:</span> value, <span class="FunctionName">criteria</span><span class="Keyword">:</span> iterator.call(context, value, index, list)}
<span class="line-numbers"> 211 </span> ).sort(<span class="FunctionArgument">(</span><span class="FunctionArgument">left, right</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 212 </span> <span class="FunctionName">a</span><span class="Keyword">:</span> left.criteria; <span class="FunctionName">b</span><span class="Keyword">:</span> right.criteria
<span class="line-numbers"> 213 </span> <span class="Keyword">if</span> a <span class="Keyword">&lt;</span> b <span class="Keyword">then</span> <span class="Keyword">-</span><span class="Number">1</span> <span class="Keyword">else</span> <span class="Keyword">if</span> a <span class="Keyword">&gt;</span> b <span class="Keyword">then</span> <span class="Number">1</span> <span class="Keyword">else</span> <span class="Number">0</span>
<span class="line-numbers"> 214 </span> )), <span class="String"><span class="String">'</span>value<span class="String">'</span></span>)
<span class="line-numbers"> 215 </span>
<span class="line-numbers"> 216 </span>
<span class="line-numbers"> 217 </span> <span class="Comment"><span class="Comment">#</span> Use a comparator function to figure out at what index an object should</span>
<span class="line-numbers"> 218 </span> <span class="Comment"><span class="Comment">#</span> be inserted so as to maintain order. Uses binary search.</span>
<span class="line-numbers"> 219 </span> <span class="FunctionName">_.sortedIndex</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, obj, iterator</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 220 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
<span class="line-numbers"> 221 </span> <span class="FunctionName">low</span><span class="Keyword">:</span> <span class="Number">0</span>
<span class="line-numbers"> 222 </span> <span class="FunctionName">high</span><span class="Keyword">:</span> array.length
<span class="line-numbers"> 223 </span> <span class="Keyword">while</span> low <span class="Keyword">&lt;</span> high
<span class="line-numbers"> 224 </span> <span class="FunctionName">mid</span><span class="Keyword">:</span> (low <span class="Keyword">+</span> high) <span class="Keyword">&gt;</span><span class="Keyword">&gt;</span> <span class="Number">1</span>
<span class="line-numbers"> 225 </span> <span class="Keyword">if</span> iterator(array[mid]) <span class="Keyword">&lt;</span> iterator(obj) <span class="Keyword">then</span> <span class="FunctionName">low</span><span class="Keyword">:</span> mid <span class="Keyword">+</span> <span class="Number">1</span> <span class="Keyword">else</span> <span class="FunctionName">high</span><span class="Keyword">:</span> mid
<span class="line-numbers"> 226 </span> low
<span class="line-numbers"> 227 </span>
<span class="line-numbers"> 228 </span>
<span class="line-numbers"> 229 </span> <span class="Comment"><span class="Comment">#</span> Convert anything iterable into a real, live array.</span>
<span class="line-numbers"> 230 </span> <span class="FunctionName">_.toArray</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">iterable</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 231 </span> <span class="Keyword">return</span> [] <span class="Keyword">if</span> (<span class="Keyword">!</span>iterable)
<span class="line-numbers"> 232 </span> <span class="Keyword">return</span> iterable.toArray() <span class="Keyword">if</span> (iterable.toArray)
<span class="line-numbers"> 233 </span> <span class="Keyword">return</span> iterable <span class="Keyword">if</span> (_.isArray(iterable))
<span class="line-numbers"> 234 </span> <span class="Keyword">return</span> slice.call(iterable) <span class="Keyword">if</span> (_.isArguments(iterable))
<span class="line-numbers"> 235 </span> _.values(iterable)
<span class="line-numbers"> 236 </span>
<span class="line-numbers"> 237 </span>
<span class="line-numbers"> 238 </span> <span class="Comment"><span class="Comment">#</span> Return the number of elements in an object.</span>
<span class="line-numbers"> 239 </span> <span class="FunctionName">_.size</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> _.toArray(obj).length
<span class="line-numbers"> 240 </span>
<span class="line-numbers"> 241 </span>
<span class="line-numbers"> 242 </span> <span class="Comment"><span class="Comment">#</span> -------------------------- Array Functions: ------------------------------</span>
<span class="line-numbers"> 243 </span>
<span class="line-numbers"> 244 </span> <span class="Comment"><span class="Comment">#</span> Get the first element of an array. Passing &quot;n&quot; will return the first N</span>
<span class="line-numbers"> 245 </span> <span class="Comment"><span class="Comment">#</span> values in the array. Aliased as &quot;head&quot;. The &quot;guard&quot; check allows it to work</span>
<span class="line-numbers"> 246 </span> <span class="Comment"><span class="Comment">#</span> with _.map.</span>
<span class="line-numbers"> 247 </span> <span class="FunctionName">_.first</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, n, guard</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 248 </span> <span class="Keyword">if</span> n <span class="Keyword">and</span> <span class="Keyword">not</span> guard <span class="Keyword">then</span> slice.call(array, <span class="Number">0</span>, n) <span class="Keyword">else</span> array[<span class="Number">0</span>]
<span class="line-numbers"> 249 </span>
<span class="line-numbers"> 250 </span>
<span class="line-numbers"> 251 </span> <span class="Comment"><span class="Comment">#</span> Returns everything but the first entry of the array. Aliased as &quot;tail&quot;.</span>
<span class="line-numbers"> 252 </span> <span class="Comment"><span class="Comment">#</span> Especially useful on the arguments object. Passing an &quot;index&quot; will return</span>
<span class="line-numbers"> 253 </span> <span class="Comment"><span class="Comment">#</span> the rest of the values in the array from that index onward. The &quot;guard&quot;</span>
<span class="line-numbers"> 254 </span> <span class="Comment"><span class="Comment">#</span> check allows it to work with _.map.</span>
<span class="line-numbers"> 255 </span> <span class="FunctionName">_.rest</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, index, guard</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 256 </span> slice.call(array, <span class="Keyword">if</span> _.isUndefined(index) <span class="Keyword">or</span> guard <span class="Keyword">then</span> <span class="Number">1</span> <span class="Keyword">else</span> index)
<span class="line-numbers"> 257 </span>
<span class="line-numbers"> 258 </span>
<span class="line-numbers"> 259 </span> <span class="Comment"><span class="Comment">#</span> Get the last element of an array.</span>
<span class="line-numbers"> 260 </span> <span class="FunctionName">_.last</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> array[array.length <span class="Keyword">-</span> <span class="Number">1</span>]
<span class="line-numbers"> 261 </span>
<span class="line-numbers"> 262 </span>
<span class="line-numbers"> 263 </span> <span class="Comment"><span class="Comment">#</span> Trim out all falsy values from an array.</span>
<span class="line-numbers"> 264 </span> <span class="FunctionName">_.compact</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> item <span class="Keyword">for</span> item <span class="Keyword">in</span> array <span class="Keyword">when</span> item
<span class="line-numbers"> 265 </span>
<span class="line-numbers"> 266 </span>
<span class="line-numbers"> 267 </span> <span class="Comment"><span class="Comment">#</span> Return a completely flattened version of an array.</span>
<span class="line-numbers"> 268 </span> <span class="FunctionName">_.flatten</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 269 </span> _.reduce array, [], <span class="FunctionArgument">(</span><span class="FunctionArgument">memo, value</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 270 </span> <span class="Keyword">return</span> memo.concat(_.flatten(value)) <span class="Keyword">if</span> _.isArray value
<span class="line-numbers"> 271 </span> memo.push value
<span class="line-numbers"> 272 </span> memo
<span class="line-numbers"> 273 </span>
<span class="line-numbers"> 274 </span>
<span class="line-numbers"> 275 </span> <span class="Comment"><span class="Comment">#</span> Return a version of the array that does not contain the specified value(s).</span>
<span class="line-numbers"> 276 </span> <span class="FunctionName">_.without</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 277 </span> <span class="FunctionName">values</span><span class="Keyword">:</span> _.rest arguments
<span class="line-numbers"> 278 </span> val <span class="Keyword">for</span> val <span class="Keyword">in</span> _.toArray(array) <span class="Keyword">when</span> <span class="Keyword">not</span> _.include values, val
<span class="line-numbers"> 279 </span>
<span class="line-numbers"> 280 </span>
<span class="line-numbers"> 281 </span> <span class="Comment"><span class="Comment">#</span> Produce a duplicate-free version of the array. If the array has already</span>
<span class="line-numbers"> 282 </span> <span class="Comment"><span class="Comment">#</span> been sorted, you have the option of using a faster algorithm.</span>
<span class="line-numbers"> 283 </span> <span class="FunctionName">_.uniq</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, isSorted</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 284 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> []
<span class="line-numbers"> 285 </span> <span class="Keyword">for</span> el, i <span class="Keyword">in</span> _.toArray array
<span class="line-numbers"> 286 </span> memo.push el <span class="Keyword">if</span> i <span class="Keyword">is</span> <span class="Number">0</span> <span class="Keyword">||</span> (<span class="Keyword">if</span> isSorted <span class="Keyword">is</span> <span class="BuiltInConstant">true</span> <span class="Keyword">then</span> _.last(memo) <span class="Keyword">isnt</span> el <span class="Keyword">else</span> <span class="Keyword">not</span> _.include(memo, el))
<span class="line-numbers"> 287 </span> memo
<span class="line-numbers"> 288 </span>
<span class="line-numbers"> 289 </span>
<span class="line-numbers"> 290 </span> <span class="Comment"><span class="Comment">#</span> Produce an array that contains every item shared between all the</span>
<span class="line-numbers"> 291 </span> <span class="Comment"><span class="Comment">#</span> passed-in arrays.</span>
<span class="line-numbers"> 292 </span> <span class="FunctionName">_.intersect</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 293 </span> <span class="FunctionName">rest</span><span class="Keyword">:</span> _.rest arguments
<span class="line-numbers"> 294 </span> _.select _.uniq(array), <span class="FunctionArgument">(</span><span class="FunctionArgument">item</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 295 </span> _.all rest, <span class="FunctionArgument">(</span><span class="FunctionArgument">other</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 296 </span> _.indexOf(other, item) <span class="Keyword">&gt;=</span> <span class="Number">0</span>
<span class="line-numbers"> 297 </span>
<span class="line-numbers"> 298 </span>
<span class="line-numbers"> 299 </span> <span class="Comment"><span class="Comment">#</span> Zip together multiple lists into a single array -- elements that share</span>
<span class="line-numbers"> 300 </span> <span class="Comment"><span class="Comment">#</span> an index go together.</span>
<span class="line-numbers"> 301 </span> <span class="FunctionName">_.zip</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 302 </span> <span class="FunctionName">length</span><span class="Keyword">:</span> _.max _.pluck arguments, <span class="String"><span class="String">'</span>length<span class="String">'</span></span>
<span class="line-numbers"> 303 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Array</span> length
<span class="line-numbers"> 304 </span> <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...length]
<span class="line-numbers"> 305 </span> results[i]<span class="Keyword">:</span> _.pluck arguments, String i
<span class="line-numbers"> 306 </span> results
<span class="line-numbers"> 307 </span>
<span class="line-numbers"> 308 </span>
<span class="line-numbers"> 309 </span> <span class="Comment"><span class="Comment">#</span> If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),</span>
<span class="line-numbers"> 310 </span> <span class="Comment"><span class="Comment">#</span> we need this function. Return the position of the first occurence of an</span>
<span class="line-numbers"> 311 </span> <span class="Comment"><span class="Comment">#</span> item in an array, or -1 if the item is not included in the array.</span>
<span class="line-numbers"> 312 </span> <span class="FunctionName">_.indexOf</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, item</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 313 </span> <span class="Keyword">return</span> array.indexOf item <span class="Keyword">if</span> nativeIndexOf <span class="Keyword">and</span> array.indexOf <span class="Keyword">is</span> nativeIndexOf
<span class="line-numbers"> 314 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> <span class="Number">0</span>; <span class="FunctionName">l</span><span class="Keyword">:</span> array.length
<span class="line-numbers"> 315 </span> <span class="Keyword">while</span> l <span class="Keyword">-</span> i
<span class="line-numbers"> 316 </span> <span class="Keyword">if</span> array[i] <span class="Keyword">is</span> item <span class="Keyword">then</span> <span class="Keyword">return</span> i <span class="Keyword">else</span> i<span class="Keyword">++</span>
<span class="line-numbers"> 317 </span> <span class="Keyword">-</span><span class="Number">1</span>
<span class="line-numbers"> 318 </span>
<span class="line-numbers"> 319 </span>
<span class="line-numbers"> 320 </span> <span class="Comment"><span class="Comment">#</span> Provide JavaScript 1.6's lastIndexOf, delegating to the native function,</span>
<span class="line-numbers"> 321 </span> <span class="Comment"><span class="Comment">#</span> if possible.</span>
<span class="line-numbers"> 322 </span> <span class="FunctionName">_.lastIndexOf</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, item</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 323 </span> <span class="Keyword">return</span> array.lastIndexOf(item) <span class="Keyword">if</span> nativeLastIndexOf <span class="Keyword">and</span> array.lastIndexOf <span class="Keyword">is</span> nativeLastIndexOf
<span class="line-numbers"> 324 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> array.length
<span class="line-numbers"> 325 </span> <span class="Keyword">while</span> i
<span class="line-numbers"> 326 </span> <span class="Keyword">if</span> array[i] <span class="Keyword">is</span> item <span class="Keyword">then</span> <span class="Keyword">return</span> i <span class="Keyword">else</span> i<span class="Keyword">--</span>
<span class="line-numbers"> 327 </span> <span class="Keyword">-</span><span class="Number">1</span>
<span class="line-numbers"> 328 </span>
<span class="line-numbers"> 329 </span>
<span class="line-numbers"> 330 </span> <span class="Comment"><span class="Comment">#</span> Generate an integer Array containing an arithmetic progression. A port of</span>
<span class="line-numbers"> 331 </span> <span class="Comment"><span class="Comment">#</span> the native Python range() function. See:</span>
<span class="line-numbers"> 332 </span> <span class="Comment"><span class="Comment">#</span> http://docs.python.org/library/functions.html#range</span>
<span class="line-numbers"> 333 </span> <span class="FunctionName">_.range</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">start, stop, step</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 334 </span> <span class="FunctionName">a</span><span class="Keyword">:</span> arguments
<span class="line-numbers"> 335 </span> <span class="FunctionName">solo</span><span class="Keyword">:</span> a.length <span class="Keyword">&lt;=</span> <span class="Number">1</span>
<span class="line-numbers"> 336 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> <span class="FunctionName">start</span><span class="Keyword">:</span> <span class="Keyword">if</span> solo <span class="Keyword">then</span> <span class="Number">0</span> <span class="Keyword">else</span> a[<span class="Number">0</span>]
<span class="line-numbers"> 337 </span> <span class="FunctionName">stop</span><span class="Keyword">:</span> <span class="Keyword">if</span> solo <span class="Keyword">then</span> a[<span class="Number">0</span>] <span class="Keyword">else</span> a[<span class="Number">1</span>]
<span class="line-numbers"> 338 </span> <span class="FunctionName">step</span><span class="Keyword">:</span> a[<span class="Number">2</span>] <span class="Keyword">or</span> <span class="Number">1</span>
<span class="line-numbers"> 339 </span> <span class="FunctionName">len</span><span class="Keyword">:</span> Math.ceil((stop <span class="Keyword">-</span> start) <span class="Keyword">/</span> step)
<span class="line-numbers"> 340 </span> <span class="Keyword">return</span> [] <span class="Keyword">if</span> len <span class="Keyword">&lt;=</span> <span class="Number">0</span>
<span class="line-numbers"> 341 </span> <span class="FunctionName">range</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Array</span> len
<span class="line-numbers"> 342 </span> <span class="FunctionName">idx</span><span class="Keyword">:</span> <span class="Number">0</span>
<span class="line-numbers"> 343 </span> <span class="Keyword">while</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 344 </span> <span class="Keyword">return</span> range <span class="Keyword">if</span> (<span class="Keyword">if</span> step <span class="Keyword">&gt;</span> <span class="Number">0</span> <span class="Keyword">then</span> i <span class="Keyword">-</span> stop <span class="Keyword">else</span> stop <span class="Keyword">-</span> i) <span class="Keyword">&gt;=</span> <span class="Number">0</span>
<span class="line-numbers"> 345 </span> range[idx]<span class="Keyword">:</span> i
<span class="line-numbers"> 346 </span> idx<span class="Keyword">++</span>
<span class="line-numbers"> 347 </span> i<span class="Keyword">+</span><span class="Keyword">=</span> step
<span class="line-numbers"> 348 </span>
<span class="line-numbers"> 349 </span>
<span class="line-numbers"> 350 </span> <span class="Comment"><span class="Comment">#</span> ----------------------- Function Functions: -----------------------------</span>
<span class="line-numbers"> 351 </span>
<span class="line-numbers"> 352 </span> <span class="Comment"><span class="Comment">#</span> Create a function bound to a given object (assigning 'this', and arguments,</span>
<span class="line-numbers"> 353 </span> <span class="Comment"><span class="Comment">#</span> optionally). Binding with arguments is also known as 'curry'.</span>
<span class="line-numbers"> 354 </span> <span class="FunctionName">_.bind</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">func, obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 355 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest arguments, <span class="Number">2</span>
<span class="line-numbers"> 356 </span> <span class="Storage">-&gt;</span> func.apply obj <span class="Keyword">or</span> root, args.concat arguments
<span class="line-numbers"> 357 </span>
<span class="line-numbers"> 358 </span>
<span class="line-numbers"> 359 </span> <span class="Comment"><span class="Comment">#</span> Bind all of an object's methods to that object. Useful for ensuring that</span>
<span class="line-numbers"> 360 </span> <span class="Comment"><span class="Comment">#</span> all callbacks defined on an object belong to it.</span>
<span class="line-numbers"> 361 </span> <span class="FunctionName">_.bindAll</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 362 </span> <span class="FunctionName">funcs</span><span class="Keyword">:</span> <span class="Keyword">if</span> arguments.length <span class="Keyword">&gt;</span> <span class="Number">1</span> <span class="Keyword">then</span> _.rest(arguments) <span class="Keyword">else</span> _.functions(obj)
<span class="line-numbers"> 363 </span> _.each funcs, <span class="FunctionArgument">(</span><span class="FunctionArgument">f</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> obj[f]<span class="Keyword">:</span> _.bind obj[f], obj
<span class="line-numbers"> 364 </span> obj
<span class="line-numbers"> 365 </span>
<span class="line-numbers"> 366 </span>
<span class="line-numbers"> 367 </span> <span class="Comment"><span class="Comment">#</span> Delays a function for the given number of milliseconds, and then calls</span>
<span class="line-numbers"> 368 </span> <span class="Comment"><span class="Comment">#</span> it with the arguments supplied.</span>
<span class="line-numbers"> 369 </span> <span class="FunctionName">_.delay</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">func, wait</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 370 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest arguments, <span class="Number">2</span>
<span class="line-numbers"> 371 </span> setTimeout((<span class="Storage">-&gt;</span> func.apply(func, args)), wait)
<span class="line-numbers"> 372 </span>
<span class="line-numbers"> 373 </span>
<span class="line-numbers"> 374 </span> <span class="Comment"><span class="Comment">#</span> Defers a function, scheduling it to run after the current call stack has</span>
<span class="line-numbers"> 375 </span> <span class="Comment"><span class="Comment">#</span> cleared.</span>
<span class="line-numbers"> 376 </span> <span class="FunctionName">_.defer</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">func</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 377 </span> _.delay.apply _, [func, <span class="Number">1</span>].concat _.rest arguments
<span class="line-numbers"> 378 </span>
<span class="line-numbers"> 379 </span>
<span class="line-numbers"> 380 </span> <span class="Comment"><span class="Comment">#</span> Returns the first function passed as an argument to the second,</span>
<span class="line-numbers"> 381 </span> <span class="Comment"><span class="Comment">#</span> allowing you to adjust arguments, run code before and after, and</span>
<span class="line-numbers"> 382 </span> <span class="Comment"><span class="Comment">#</span> conditionally execute the original function.</span>
<span class="line-numbers"> 383 </span> <span class="FunctionName">_.wrap</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">func, wrapper</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 384 </span> <span class="Storage">-&gt;</span> wrapper.apply wrapper, [func].concat arguments
<span class="line-numbers"> 385 </span>
<span class="line-numbers"> 386 </span>
<span class="line-numbers"> 387 </span> <span class="Comment"><span class="Comment">#</span> Returns a function that is the composition of a list of functions, each</span>
<span class="line-numbers"> 388 </span> <span class="Comment"><span class="Comment">#</span> consuming the return value of the function that follows.</span>
<span class="line-numbers"> 389 </span> <span class="FunctionName">_.compose</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 390 </span> <span class="FunctionName">funcs</span><span class="Keyword">:</span> arguments
<span class="line-numbers"> 391 </span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 392 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> arguments
<span class="line-numbers"> 393 </span> <span class="Keyword">for</span> i <span class="Keyword">in</span> [(funcs.length <span class="Keyword">-</span> <span class="Number">1</span>)..<span class="Number">0</span>]
<span class="line-numbers"> 394 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> [funcs[i].apply(<span class="Variable">this</span>, args)]
<span class="line-numbers"> 395 </span> args[<span class="Number">0</span>]
<span class="line-numbers"> 396 </span>
<span class="line-numbers"> 397 </span>
<span class="line-numbers"> 398 </span> <span class="Comment"><span class="Comment">#</span> ------------------------- Object Functions: ----------------------------</span>
<span class="line-numbers"> 399 </span>
<span class="line-numbers"> 400 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the names of an object's properties.</span>
<span class="line-numbers"> 401 </span> <span class="FunctionName">_.keys</span><span class="Keyword">:</span> nativeKeys <span class="Keyword">or</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 402 </span> <span class="Keyword">return</span> _.range <span class="Number">0</span>, obj.length <span class="Keyword">if</span> _.isArray(obj)
<span class="line-numbers"> 403 </span> key <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
<span class="line-numbers"> 404 </span>
<span class="line-numbers"> 405 </span>
<span class="line-numbers"> 406 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the values of an object's properties.</span>
<span class="line-numbers"> 407 </span> <span class="FunctionName">_.values</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 408 </span> _.map obj, _.identity
<span class="line-numbers"> 409 </span>
<span class="line-numbers"> 410 </span>
<span class="line-numbers"> 411 </span> <span class="Comment"><span class="Comment">#</span> Return a sorted list of the function names available in Underscore.</span>
<span class="line-numbers"> 412 </span> <span class="FunctionName">_.functions</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 413 </span> _.filter(_.keys(obj), <span class="FunctionArgument">(</span><span class="FunctionArgument">key</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> _.isFunction(obj[key])).sort()
<span class="line-numbers"> 414 </span>
<span class="line-numbers"> 415 </span>
<span class="line-numbers"> 416 </span> <span class="Comment"><span class="Comment">#</span> Extend a given object with all of the properties in a source object.</span>
<span class="line-numbers"> 417 </span> <span class="FunctionName">_.extend</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">destination, source</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 418 </span> (destination[key]<span class="Keyword">:</span> val) <span class="Keyword">for</span> key, val <span class="Keyword">of</span> source
<span class="line-numbers"> 419 </span> destination
<span class="line-numbers"> 420 </span>
<span class="line-numbers"> 421 </span>
<span class="line-numbers"> 422 </span> <span class="Comment"><span class="Comment">#</span> Create a (shallow-cloned) duplicate of an object.</span>
<span class="line-numbers"> 423 </span> <span class="FunctionName">_.clone</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 424 </span> <span class="Keyword">return</span> obj.slice <span class="Number">0</span> <span class="Keyword">if</span> _.isArray obj
<span class="line-numbers"> 425 </span> _.extend {}, obj
<span class="line-numbers"> 426 </span>
<span class="line-numbers"> 427 </span>
<span class="line-numbers"> 428 </span> <span class="Comment"><span class="Comment">#</span> Invokes interceptor with the obj, and then returns obj.</span>
<span class="line-numbers"> 429 </span> <span class="Comment"><span class="Comment">#</span> The primary purpose of this method is to &quot;tap into&quot; a method chain, in order to perform operations on intermediate results within the chain.</span>
<span class="line-numbers"> 430 </span> <span class="FunctionName">_.tap</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, interceptor</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 431 </span> interceptor obj
<span class="line-numbers"> 432 </span> obj
<span class="line-numbers"> 433 </span>
<span class="line-numbers"> 434 </span>
<span class="line-numbers"> 435 </span> <span class="Comment"><span class="Comment">#</span> Perform a deep comparison to check if two objects are equal.</span>
<span class="line-numbers"> 436 </span> <span class="FunctionName">_.isEqual</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">a, b</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 437 </span> <span class="Comment"><span class="Comment">#</span> Check object identity.</span>
<span class="line-numbers"> 438 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> a <span class="Keyword">is</span> b
<span class="line-numbers"> 439 </span> <span class="Comment"><span class="Comment">#</span> Different types?</span>
<span class="line-numbers"> 440 </span> <span class="FunctionName">atype</span><span class="Keyword">:</span> <span class="Keyword">typeof</span>(a); <span class="FunctionName">btype</span><span class="Keyword">:</span> <span class="Keyword">typeof</span>(b)
<span class="line-numbers"> 441 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> atype <span class="Keyword">isnt</span> btype
<span class="line-numbers"> 442 </span> <span class="Comment"><span class="Comment">#</span> Basic equality test (watch out for coercions).</span>
<span class="line-numbers"> 443 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> <span class="String"><span class="String">`</span>a == b<span class="String">`</span></span>
<span class="line-numbers"> 444 </span> <span class="Comment"><span class="Comment">#</span> One is falsy and the other truthy.</span>
<span class="line-numbers"> 445 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> (<span class="Keyword">!</span>a <span class="Keyword">and</span> b) <span class="Keyword">or</span> (a <span class="Keyword">and</span> <span class="Keyword">!</span>b)
<span class="line-numbers"> 446 </span> <span class="Comment"><span class="Comment">#</span> One of them implements an isEqual()?</span>
<span class="line-numbers"> 447 </span> <span class="Keyword">return</span> a.isEqual(b) <span class="Keyword">if</span> a.isEqual
<span class="line-numbers"> 448 </span> <span class="Comment"><span class="Comment">#</span> Check dates' integer values.</span>
<span class="line-numbers"> 449 </span> <span class="Keyword">return</span> a.getTime() <span class="Keyword">is</span> b.getTime() <span class="Keyword">if</span> _.isDate(a) <span class="Keyword">and</span> _.isDate(b)
<span class="line-numbers"> 450 </span> <span class="Comment"><span class="Comment">#</span> Both are NaN?</span>
<span class="line-numbers"> 451 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> _.isNaN(a) <span class="Keyword">and</span> _.isNaN(b)
<span class="line-numbers"> 452 </span> <span class="Comment"><span class="Comment">#</span> Compare regular expressions.</span>
<span class="line-numbers"> 453 </span> <span class="Keyword">if</span> _.isRegExp(a) <span class="Keyword">and</span> _.isRegExp(b)
<span class="line-numbers"> 454 </span> <span class="Keyword">return</span> a.source <span class="Keyword">is</span> b.source <span class="Keyword">and</span>
<span class="line-numbers"> 455 </span> a.global <span class="Keyword">is</span> b.global <span class="Keyword">and</span>
<span class="line-numbers"> 456 </span> a.ignoreCase <span class="Keyword">is</span> b.ignoreCase <span class="Keyword">and</span>
<span class="line-numbers"> 457 </span> a.multiline <span class="Keyword">is</span> b.multiline
<span class="line-numbers"> 458 </span> <span class="Comment"><span class="Comment">#</span> If a is not an object by this point, we can't handle it.</span>
<span class="line-numbers"> 459 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> atype <span class="Keyword">isnt</span> <span class="String"><span class="String">'</span>object<span class="String">'</span></span>
<span class="line-numbers"> 460 </span> <span class="Comment"><span class="Comment">#</span> Check for different array lengths before comparing contents.</span>
<span class="line-numbers"> 461 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> a.length <span class="Keyword">and</span> (a.length <span class="Keyword">isnt</span> b.length)
<span class="line-numbers"> 462 </span> <span class="Comment"><span class="Comment">#</span> Nothing else worked, deep compare the contents.</span>
<span class="line-numbers"> 463 </span> <span class="FunctionName">aKeys</span><span class="Keyword">:</span> _.keys(a); <span class="FunctionName">bKeys</span><span class="Keyword">:</span> _.keys(b)
<span class="line-numbers"> 464 </span> <span class="Comment"><span class="Comment">#</span> Different object sizes?</span>
<span class="line-numbers"> 465 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> aKeys.length <span class="Keyword">isnt</span> bKeys.length
<span class="line-numbers"> 466 </span> <span class="Comment"><span class="Comment">#</span> Recursive comparison of contents.</span>
<span class="line-numbers"> 467 </span> (<span class="Keyword">return</span> <span class="BuiltInConstant">false</span>) <span class="Keyword">for</span> key, val <span class="Keyword">of</span> a <span class="Keyword">when</span> <span class="Keyword">!</span>_.isEqual(val, b[key])
<span class="line-numbers"> 468 </span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 469 </span>
<span class="line-numbers"> 470 </span>
<span class="line-numbers"> 471 </span> <span class="Comment"><span class="Comment">#</span> Is a given array or object empty?</span>
<span class="line-numbers"> 472 </span> <span class="FunctionName">_.isEmpty</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 473 </span> <span class="Keyword">return</span> obj.length <span class="Keyword">is</span> <span class="Number">0</span> <span class="Keyword">if</span> _.isArray obj
<span class="line-numbers"> 474 </span> (<span class="Keyword">return</span> <span class="BuiltInConstant">false</span>) <span class="Keyword">for</span> key <span class="Keyword">of</span> obj <span class="Keyword">when</span> hasOwnProperty.call(obj, key)
<span class="line-numbers"> 475 </span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 476 </span>
<span class="line-numbers"> 477 </span>
<span class="line-numbers"> 478 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a DOM element?</span>
<span class="line-numbers"> 479 </span> <span class="FunctionName">_.isElement</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> obj <span class="Keyword">and</span> obj.nodeType <span class="Keyword">is</span> <span class="Number">1</span>
<span class="line-numbers"> 480 </span>
<span class="line-numbers"> 481 </span>
<span class="line-numbers"> 482 </span> <span class="Comment"><span class="Comment">#</span> Is a given value an array?</span>
<span class="line-numbers"> 483 </span> <span class="FunctionName">_.isArray</span><span class="Keyword">:</span> nativeIsArray <span class="Keyword">or</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.concat <span class="Keyword">and</span> obj.unshift)
<span class="line-numbers"> 484 </span>
<span class="line-numbers"> 485 </span>
<span class="line-numbers"> 486 </span> <span class="Comment"><span class="Comment">#</span> Is a given variable an arguments object?</span>
<span class="line-numbers"> 487 </span> <span class="FunctionName">_.isArguments</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> obj <span class="Keyword">and</span> _.isNumber(obj.length) <span class="Keyword">and</span> <span class="Keyword">not</span> obj.concat <span class="Keyword">and</span>
<span class="line-numbers"> 488 </span> <span class="Keyword">not</span> obj.substr <span class="Keyword">and</span> <span class="Keyword">not</span> obj.apply <span class="Keyword">and</span> <span class="Keyword">not</span> propertyIsEnumerable.call(obj, <span class="String"><span class="String">'</span>length<span class="String">'</span></span>)
<span class="line-numbers"> 489 </span>
<span class="line-numbers"> 490 </span>
<span class="line-numbers"> 491 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a function?</span>
<span class="line-numbers"> 492 </span> <span class="FunctionName">_.isFunction</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.constructor <span class="Keyword">and</span> obj.call <span class="Keyword">and</span> obj.apply)
<span class="line-numbers"> 493 </span>
<span class="line-numbers"> 494 </span>
<span class="line-numbers"> 495 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a string?</span>
<span class="line-numbers"> 496 </span> <span class="FunctionName">_.isString</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">is</span> <span class="String"><span class="String">'</span><span class="String">'</span></span> <span class="Keyword">or</span> (obj <span class="Keyword">and</span> obj.charCodeAt <span class="Keyword">and</span> obj.substr))
<span class="line-numbers"> 497 </span>
<span class="line-numbers"> 498 </span>
<span class="line-numbers"> 499 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a number?</span>
<span class="line-numbers"> 500 </span> <span class="FunctionName">_.isNumber</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> (obj <span class="Keyword">is</span> <span class="Keyword">+</span>obj) <span class="Keyword">or</span> toString.call(obj) <span class="Keyword">is</span> <span class="String"><span class="String">'</span>[object Number]<span class="String">'</span></span>
<span class="line-numbers"> 501 </span>
<span class="line-numbers"> 502 </span>
<span class="line-numbers"> 503 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a boolean?</span>
<span class="line-numbers"> 504 </span> <span class="FunctionName">_.isBoolean</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> obj <span class="Keyword">is</span> <span class="BuiltInConstant">true</span> <span class="Keyword">or</span> obj <span class="Keyword">is</span> <span class="BuiltInConstant">false</span>
<span class="line-numbers"> 505 </span>
<span class="line-numbers"> 506 </span>
<span class="line-numbers"> 507 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a Date?</span>
<span class="line-numbers"> 508 </span> <span class="FunctionName">_.isDate</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.getTimezoneOffset <span class="Keyword">and</span> obj.setUTCFullYear)
<span class="line-numbers"> 509 </span>
<span class="line-numbers"> 510 </span>
<span class="line-numbers"> 511 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a regular expression?</span>
<span class="line-numbers"> 512 </span> <span class="FunctionName">_.isRegExp</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.exec <span class="Keyword">and</span> (obj.ignoreCase <span class="Keyword">or</span> obj.ignoreCase <span class="Keyword">is</span> <span class="BuiltInConstant">false</span>))
<span class="line-numbers"> 513 </span>
<span class="line-numbers"> 514 </span>
<span class="line-numbers"> 515 </span> <span class="Comment"><span class="Comment">#</span> Is the given value NaN -- this one is interesting. NaN != NaN, and</span>
<span class="line-numbers"> 516 </span> <span class="Comment"><span class="Comment">#</span> isNaN(undefined) == true, so we make sure it's a number first.</span>
<span class="line-numbers"> 517 </span> <span class="FunctionName">_.isNaN</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> _.isNumber(obj) <span class="Keyword">and</span> window.isNaN(obj)
<span class="line-numbers"> 518 </span>
<span class="line-numbers"> 519 </span>
<span class="line-numbers"> 520 </span> <span class="Comment"><span class="Comment">#</span> Is a given value equal to null?</span>
<span class="line-numbers"> 521 </span> <span class="FunctionName">_.isNull</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> obj <span class="Keyword">is</span> <span class="BuiltInConstant">null</span>
<span class="line-numbers"> 522 </span>
<span class="line-numbers"> 523 </span>
<span class="line-numbers"> 524 </span> <span class="Comment"><span class="Comment">#</span> Is a given variable undefined?</span>
<span class="line-numbers"> 525 </span> <span class="FunctionName">_.isUndefined</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="Keyword">typeof</span> obj <span class="Keyword">is</span> <span class="String"><span class="String">'</span>undefined<span class="String">'</span></span>
<span class="line-numbers"> 526 </span>
<span class="line-numbers"> 527 </span>
<span class="line-numbers"> 528 </span> <span class="Comment"><span class="Comment">#</span> -------------------------- Utility Functions: --------------------------</span>
<span class="line-numbers"> 529 </span>
<span class="line-numbers"> 530 </span> <span class="Comment"><span class="Comment">#</span> Run Underscore.js in noConflict mode, returning the '_' variable to its</span>
<span class="line-numbers"> 531 </span> <span class="Comment"><span class="Comment">#</span> previous owner. Returns a reference to the Underscore object.</span>
<span class="line-numbers"> 532 </span> <span class="FunctionName">_.noConflict</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 533 </span> <span class="FunctionName">root._</span><span class="Keyword">:</span> previousUnderscore
<span class="line-numbers"> 534 </span> <span class="Variable">this</span>
<span class="line-numbers"> 535 </span>
<span class="line-numbers"> 536 </span>
<span class="line-numbers"> 537 </span> <span class="Comment"><span class="Comment">#</span> Keep the identity function around for default iterators.</span>
<span class="line-numbers"> 538 </span> <span class="FunctionName">_.identity</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">value</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> value
<span class="line-numbers"> 539 </span>
<span class="line-numbers"> 540 </span>
<span class="line-numbers"> 541 </span> <span class="Comment"><span class="Comment">#</span> Run a function n times.</span>
<span class="line-numbers"> 542 </span> <span class="FunctionName">_.times</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">n, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 543 </span> iterator.call(context, i) <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...n]
<span class="line-numbers"> 544 </span>
<span class="line-numbers"> 545 </span>
<span class="line-numbers"> 546 </span> <span class="Comment"><span class="Comment">#</span> Break out of the middle of an iteration.</span>
<span class="line-numbers"> 547 </span> <span class="FunctionName">_.breakLoop</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span> <span class="Keyword">throw</span> breaker
<span class="line-numbers"> 548 </span>
<span class="line-numbers"> 549 </span>
<span class="line-numbers"> 550 </span> <span class="Comment"><span class="Comment">#</span> Add your own custom functions to the Underscore object, ensuring that</span>
<span class="line-numbers"> 551 </span> <span class="Comment"><span class="Comment">#</span> they're correctly added to the OOP wrapper as well.</span>
<span class="line-numbers"> 552 </span> <span class="FunctionName">_.mixin</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 553 </span> <span class="Keyword">for</span> name <span class="Keyword">in</span> _.functions(obj)
<span class="line-numbers"> 554 </span> addToWrapper name, _[name]<span class="Keyword">:</span> obj[name]
<span class="line-numbers"> 555 </span>
<span class="line-numbers"> 556 </span>
<span class="line-numbers"> 557 </span> <span class="Comment"><span class="Comment">#</span> Generate a unique integer id (unique within the entire client session).</span>
<span class="line-numbers"> 558 </span> <span class="Comment"><span class="Comment">#</span> Useful for temporary DOM ids.</span>
<span class="line-numbers"> 559 </span> <span class="FunctionName">idCounter</span><span class="Keyword">:</span> <span class="Number">0</span>
<span class="line-numbers"> 560 </span> <span class="FunctionName">_.uniqueId</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">prefix</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 561 </span> (prefix <span class="Keyword">or</span> <span class="String"><span class="String">'</span><span class="String">'</span></span>) <span class="Keyword">+</span> idCounter<span class="Keyword">++</span>
<span class="line-numbers"> 562 </span>
<span class="line-numbers"> 563 </span>
<span class="line-numbers"> 564 </span> <span class="Comment"><span class="Comment">#</span> By default, Underscore uses ERB-style template delimiters, change the</span>
<span class="line-numbers"> 565 </span> <span class="Comment"><span class="Comment">#</span> following template settings to use alternative delimiters.</span>
<span class="line-numbers"> 566 </span> <span class="FunctionName">_.templateSettings</span><span class="Keyword">:</span> {
<span class="line-numbers"> 567 </span> <span class="FunctionName">start</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span>&lt;%<span class="String">'</span></span>
<span class="line-numbers"> 568 </span> <span class="FunctionName">end</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span>%&gt;<span class="String">'</span></span>
<span class="line-numbers"> 569 </span> <span class="FunctionName">interpolate</span><span class="Keyword">:</span><span class="String"> <span class="String">/</span>&lt;%=(.+?)%&gt;<span class="String">/</span>g</span>
<span class="line-numbers"> 570 </span> }
<span class="line-numbers"> 571 </span>
<span class="line-numbers"> 572 </span>
<span class="line-numbers"> 573 </span> <span class="Comment"><span class="Comment">#</span> JavaScript templating a-la ERB, pilfered from John Resig's</span>
<span class="line-numbers"> 574 </span> <span class="Comment"><span class="Comment">#</span> &quot;Secrets of the JavaScript Ninja&quot;, page 83.</span>
<span class="line-numbers"> 575 </span> <span class="Comment"><span class="Comment">#</span> Single-quote fix from Rick Strahl's version.</span>
<span class="line-numbers"> 576 </span> <span class="FunctionName">_.template</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">str, data</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 577 </span> <span class="FunctionName">c</span><span class="Keyword">:</span> _.templateSettings
<span class="line-numbers"> 578 </span> <span class="FunctionName">endMatch</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">RegExp</span>(<span class="String"><span class="String">&quot;</span>'(?=[^<span class="String">&quot;</span></span><span class="Keyword">+</span>c.end.substr(<span class="Number">0</span>, <span class="Number">1</span>)<span class="Keyword">+</span><span class="String"><span class="String">&quot;</span>]*<span class="String">&quot;</span></span><span class="Keyword">+</span>escapeRegExp(c.end)<span class="Keyword">+</span><span class="String"><span class="String">&quot;</span>)<span class="String">&quot;</span></span>,<span class="String"><span class="String">&quot;</span>g<span class="String">&quot;</span></span>)
<span class="line-numbers"> 579 </span> <span class="FunctionName">fn</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Function</span> <span class="String"><span class="String">'</span>obj<span class="String">'</span></span>,
<span class="line-numbers"> 580 </span> <span class="String"><span class="String">'</span>var p=[],print=function(){p.push.apply(p,arguments);};<span class="String">'</span></span> <span class="Keyword">+</span>
<span class="line-numbers"> 581 </span> <span class="String"><span class="String">'</span>with(obj){p.push(<span class="UserDefinedConstant">\'</span><span class="String">'</span></span> <span class="Keyword">+</span>
<span class="line-numbers"> 582 </span> str.replace(<span class="String"><span class="String">/</span>[<span class="UserDefinedConstant">\r</span><span class="UserDefinedConstant">\t</span><span class="UserDefinedConstant">\n</span>]<span class="String">/</span>g</span>, <span class="String"><span class="String">&quot;</span> <span class="String">&quot;</span></span>)
<span class="line-numbers"> 583 </span> .replace(endMatch,<span class="String"><span class="String">&quot;</span><span class="UserDefinedConstant">\t</span><span class="String">&quot;</span></span>)
<span class="line-numbers"> 584 </span> .split(<span class="String"><span class="String">&quot;</span>'<span class="String">&quot;</span></span>).join(<span class="String"><span class="String">&quot;</span><span class="UserDefinedConstant">\\</span>'<span class="String">&quot;</span></span>)
<span class="line-numbers"> 585 </span> .split(<span class="String"><span class="String">&quot;</span><span class="UserDefinedConstant">\t</span><span class="String">&quot;</span></span>).join(<span class="String"><span class="String">&quot;</span>'<span class="String">&quot;</span></span>)
<span class="line-numbers"> 586 </span> .replace(c.interpolate, <span class="String"><span class="String">&quot;</span>',$1,'<span class="String">&quot;</span></span>)
<span class="line-numbers"> 587 </span> .split(c.start).join(<span class="String"><span class="String">&quot;</span>');<span class="String">&quot;</span></span>)
<span class="line-numbers"> 588 </span> .split(c.end).join(<span class="String"><span class="String">&quot;</span>p.push('<span class="String">&quot;</span></span>) <span class="Keyword">+</span>
<span class="line-numbers"> 589 </span> <span class="String"><span class="String">&quot;</span>');}return p.join('');<span class="String">&quot;</span></span>
<span class="line-numbers"> 590 </span> <span class="Keyword">if</span> data <span class="Keyword">then</span> fn(data) <span class="Keyword">else</span> fn
<span class="line-numbers"> 591 </span>
<span class="line-numbers"> 592 </span>
<span class="line-numbers"> 593 </span> <span class="Comment"><span class="Comment">#</span> ------------------------------- Aliases ----------------------------------</span>
<span class="line-numbers"> 594 </span>
<span class="line-numbers"> 595 </span> <span class="FunctionName">_.forEach</span><span class="Keyword">:</span> _.each
<span class="line-numbers"> 596 </span> <span class="FunctionName">_.foldl</span><span class="Keyword">:</span> <span class="FunctionName">_.inject</span><span class="Keyword">:</span> _.reduce
<span class="line-numbers"> 597 </span> <span class="FunctionName">_.foldr</span><span class="Keyword">:</span> _.reduceRight
<span class="line-numbers"> 598 </span> <span class="FunctionName">_.select</span><span class="Keyword">:</span> _.filter
<span class="line-numbers"> 599 </span> <span class="FunctionName">_.all</span><span class="Keyword">:</span> _.every
<span class="line-numbers"> 600 </span> <span class="FunctionName">_.any</span><span class="Keyword">:</span> _.some
<span class="line-numbers"> 601 </span> <span class="FunctionName">_.head</span><span class="Keyword">:</span> _.first
<span class="line-numbers"> 602 </span> <span class="FunctionName">_.tail</span><span class="Keyword">:</span> _.rest
<span class="line-numbers"> 603 </span> <span class="FunctionName">_.methods</span><span class="Keyword">:</span> _.functions
<span class="line-numbers"> 604 </span>
<span class="line-numbers"> 605 </span>
<span class="line-numbers"> 606 </span> <span class="Comment"><span class="Comment">#</span> ------------------------ Setup the OOP Wrapper: --------------------------</span>
<span class="line-numbers"> 607 </span>
<span class="line-numbers"> 608 </span> <span class="Comment"><span class="Comment">#</span> If Underscore is called as a function, it returns a wrapped object that</span>
<span class="line-numbers"> 609 </span> <span class="Comment"><span class="Comment">#</span> can be used OO-style. This wrapper holds altered versions of all the</span>
<span class="line-numbers"> 610 </span> <span class="Comment"><span class="Comment">#</span> underscore functions. Wrapped objects may be chained.</span>
<span class="line-numbers"> 611 </span> <span class="FunctionName">wrapper</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 612 </span> <span class="FunctionName">this._wrapped</span><span class="Keyword">:</span> obj
<span class="line-numbers"> 613 </span> <span class="Variable">this</span>
<span class="line-numbers"> 614 </span>
<span class="line-numbers"> 615 </span>
<span class="line-numbers"> 616 </span> <span class="Comment"><span class="Comment">#</span> Helper function to continue chaining intermediate results.</span>
<span class="line-numbers"> 617 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, chain</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 618 </span> <span class="Keyword">if</span> chain <span class="Keyword">then</span> _(obj).chain() <span class="Keyword">else</span> obj
<span class="line-numbers"> 619 </span>
<span class="line-numbers"> 620 </span>
<span class="line-numbers"> 621 </span> <span class="Comment"><span class="Comment">#</span> A method to easily add functions to the OOP wrapper.</span>
<span class="line-numbers"> 622 </span> <span class="FunctionName">addToWrapper</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">name, func</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 623 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 624 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.toArray arguments
<span class="line-numbers"> 625 </span> unshift.call args, <span class="Variable">this</span>._wrapped
<span class="line-numbers"> 626 </span> result func.apply(_, args), <span class="Variable">this</span>._chain
<span class="line-numbers"> 627 </span>
<span class="line-numbers"> 628 </span>
<span class="line-numbers"> 629 </span> <span class="Comment"><span class="Comment">#</span> Add all of the Underscore functions to the wrapper object.</span>
<span class="line-numbers"> 630 </span> _.mixin _
<span class="line-numbers"> 631 </span>
<span class="line-numbers"> 632 </span>
<span class="line-numbers"> 633 </span> <span class="Comment"><span class="Comment">#</span> Add all mutator Array functions to the wrapper.</span>
<span class="line-numbers"> 634 </span> _.each [<span class="String"><span class="String">'</span>pop<span class="String">'</span></span>, <span class="String"><span class="String">'</span>push<span class="String">'</span></span>, <span class="String"><span class="String">'</span>reverse<span class="String">'</span></span>, <span class="String"><span class="String">'</span>shift<span class="String">'</span></span>, <span class="String"><span class="String">'</span>sort<span class="String">'</span></span>, <span class="String"><span class="String">'</span>splice<span class="String">'</span></span>, <span class="String"><span class="String">'</span>unshift<span class="String">'</span></span>], <span class="FunctionArgument">(</span><span class="FunctionArgument">name</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 635 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
<span class="line-numbers"> 636 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 637 </span> method.apply(<span class="Variable">this</span>._wrapped, arguments)
<span class="line-numbers"> 638 </span> result(<span class="Variable">this</span>._wrapped, <span class="Variable">this</span>._chain)
<span class="line-numbers"> 639 </span>
<span class="line-numbers"> 640 </span>
<span class="line-numbers"> 641 </span> <span class="Comment"><span class="Comment">#</span> Add all accessor Array functions to the wrapper.</span>
<span class="line-numbers"> 642 </span> _.each [<span class="String"><span class="String">'</span>concat<span class="String">'</span></span>, <span class="String"><span class="String">'</span>join<span class="String">'</span></span>, <span class="String"><span class="String">'</span>slice<span class="String">'</span></span>], <span class="FunctionArgument">(</span><span class="FunctionArgument">name</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 643 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
<span class="line-numbers"> 644 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 645 </span> result(method.apply(<span class="Variable">this</span>._wrapped, arguments), <span class="Variable">this</span>._chain)
<span class="line-numbers"> 646 </span>
<span class="line-numbers"> 647 </span>
<span class="line-numbers"> 648 </span> <span class="Comment"><span class="Comment">#</span> Start chaining a wrapped Underscore object.</span>
<span class="line-numbers"> 649 </span> <span class="FunctionName">wrapper::chain</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 650 </span> <span class="FunctionName">this._chain</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 651 </span> <span class="Variable">this</span>
<span class="line-numbers"> 652 </span>
<span class="line-numbers"> 653 </span>
<span class="line-numbers"> 654 </span> <span class="Comment"><span class="Comment">#</span> Extracts the result from a wrapped and chained object.</span>
<span class="line-numbers"> 655 </span> <span class="FunctionName">wrapper::value</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span> <span class="Variable">this</span>._wrapped
</pre>
</body>
</html>

View File

@@ -1,28 +1,33 @@
# Underscore.coffee
# (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
# Underscore is freely distributable under the terms of the MIT license.
# Portions of Underscore are inspired by or borrowed from Prototype.js,
# Oliver Steele's Functional, and John Resig's Micro-Templating.
# **Underscore.coffee
# (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.**
# Underscore is freely distributable under the terms of the
# [MIT license](http://en.wikipedia.org/wiki/MIT_License).
# Portions of Underscore are inspired by or borrowed from
# [Prototype.js](http://prototypejs.org/api), Oliver Steele's
# [Functional](http://osteele.com), and John Resig's
# [Micro-Templating](http://ejohn.com).
# For all details and documentation:
# http://documentcloud.github.com/underscore/
# ------------------------- Baseline setup ---------------------------------
# Baseline setup
# --------------
# Establish the root object, "window" in the browser, or "global" on the server.
# Establish the root object, `window` in the browser, or `global` on the server.
root: this
# Save the previous value of the "_" variable.
# Save the previous value of the `_` variable.
previousUnderscore: root._
# Establish the object that gets thrown to break out of a loop iteration.
# `StopIteration` is SOP on Mozilla.
breaker: if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
# Quick regexp-escaping function, because JS doesn't have RegExp.escape().
# Helper function to escape **RegExp** contents, because JS doesn't have one.
escapeRegExp: (string) -> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
@@ -31,7 +36,7 @@
ObjProto: Object.prototype
#Create quick reference variables for speed access to core prototypes.
# Create quick reference variables for speed access to core prototypes.
slice: ArrayProto.slice
unshift: ArrayProto.unshift
toString: ObjProto.toString
@@ -39,7 +44,7 @@
propertyIsEnumerable: ObjProto.propertyIsEnumerable
# All ECMA5 native implementations we hope to use are declared here.
# All **ECMA5** native implementations we hope to use are declared here.
nativeForEach: ArrayProto.forEach
nativeMap: ArrayProto.map
nativeReduce: ArrayProto.reduce
@@ -57,7 +62,7 @@
_: (obj) -> new wrapper(obj)
# Export the Underscore object for CommonJS.
# Export the Underscore object for **CommonJS**.
if typeof(exports) != 'undefined' then exports._: _
@@ -66,13 +71,14 @@
# Current version.
_.VERSION: '0.6.0'
_.VERSION: '1.0.2'
# ------------------------ Collection Functions: ---------------------------
# Collection Functions
# --------------------
# The cornerstone, an each implementation.
# Handles objects implementing forEach, arrays, and raw objects.
# The cornerstone, an **each** implementation.
# Handles objects implementing **forEach**, arrays, and raw objects.
_.each: (obj, iterator, context) ->
try
if nativeForEach and obj.forEach is nativeForEach
@@ -87,7 +93,7 @@
# Return the results of applying the iterator to each element. Use JavaScript
# 1.6's version of map, if possible.
# 1.6's version of **map**, if possible.
_.map: (obj, iterator, context) ->
return obj.map(iterator, context) if nativeMap and obj.map is nativeMap
results: []
@@ -96,8 +102,8 @@
results
# Reduce builds up a single result from a list of values. Also known as
# inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
# **Reduce** builds up a single result from a list of values. Also known as
# **inject**, or **foldl**. Uses JavaScript 1.8's version of **reduce**, if possible.
_.reduce: (obj, memo, iterator, context) ->
return obj.reduce(_.bind(iterator, context), memo) if nativeReduce and obj.reduce is nativeReduce
_.each obj, (value, index, list) ->
@@ -105,8 +111,8 @@
memo
# The right-associative version of reduce, also known as foldr. Uses
# JavaScript 1.8's version of reduceRight, if available.
# The right-associative version of **reduce**, also known as **foldr**. Uses
# JavaScript 1.8's version of **reduceRight**, if available.
_.reduceRight: (obj, memo, iterator, context) ->
return obj.reduceRight(_.bind(iterator, context), memo) if nativeReduceRight and obj.reduceRight is nativeReduceRight
_.each _.clone(_.toArray(obj)).reverse(), (value, index) ->
@@ -125,7 +131,7 @@
# Return all the elements that pass a truth test. Use JavaScript 1.6's
# filter(), if it exists.
# **filter**, if it exists.
_.filter: (obj, iterator, context) ->
return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter
results: []
@@ -143,7 +149,7 @@
# Determine whether all of the elements match a truth test. Delegate to
# JavaScript 1.6's every(), if it is present.
# JavaScript 1.6's **every**, if it is present.
_.every: (obj, iterator, context) ->
iterator ||= _.identity
return obj.every iterator, context if nativeEvery and obj.every is nativeEvery
@@ -154,7 +160,7 @@
# Determine if at least one element in the object matches a truth test. Use
# JavaScript 1.6's some(), if it exists.
# JavaScript 1.6's **some**, if it exists.
_.some: (obj, iterator, context) ->
iterator ||= _.identity
return obj.some iterator, context if nativeSome and obj.some is nativeSome
@@ -165,7 +171,7 @@
# Determine if a given value is included in the array or object,
# based on '==='.
# based on `===`.
_.include: (obj, target) ->
return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf
for key, val of obj
@@ -179,7 +185,7 @@
(if method then val[method] else val).apply(val, args) for val in obj
# Convenience version of a common use case of map: fetching a property.
# Convenience version of a common use case of **map**: fetching a property.
_.pluck: (obj, key) ->
_.map(obj, (val) -> val[key])
@@ -239,19 +245,20 @@
_.size: (obj) -> _.toArray(obj).length
# -------------------------- Array Functions: ------------------------------
# Array Functions
# ---------------
# Get the first element of an array. Passing "n" will return the first N
# values in the array. Aliased as "head". The "guard" check allows it to work
# with _.map.
# Get the first element of an array. Passing `n` will return the first N
# values in the array. Aliased as **head**. The `guard` check allows it to work
# with **map**.
_.first: (array, n, guard) ->
if n and not guard then slice.call(array, 0, n) else array[0]
# Returns everything but the first entry of the array. Aliased as "tail".
# Especially useful on the arguments object. Passing an "index" will return
# the rest of the values in the array from that index onward. The "guard"
# check allows it to work with _.map.
# Returns everything but the first entry of the array. Aliased as **tail**.
# Especially useful on the arguments object. Passing an `index` will return
# the rest of the values in the array from that index onward. The `guard`
# check allows it to work with **map**.
_.rest: (array, index, guard) ->
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
@@ -306,7 +313,7 @@
results
# If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
# If the browser doesn't supply us with **indexOf** (I'm looking at you, MSIE),
# we need this function. Return the position of the first occurence of an
# item in an array, or -1 if the item is not included in the array.
_.indexOf: (array, item) ->
@@ -317,7 +324,7 @@
-1
# Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
# Provide JavaScript 1.6's **lastIndexOf**, delegating to the native function,
# if possible.
_.lastIndexOf: (array, item) ->
return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf
@@ -328,8 +335,7 @@
# Generate an integer Array containing an arithmetic progression. A port of
# the native Python range() function. See:
# http://docs.python.org/library/functions.html#range
# [the native Python **range** function](http://docs.python.org/library/functions.html#range).
_.range: (start, stop, step) ->
a: arguments
solo: a.length <= 1
@@ -347,10 +353,11 @@
i+= step
# ----------------------- Function Functions: -----------------------------
# Function Functions
# ------------------
# Create a function bound to a given object (assigning 'this', and arguments,
# optionally). Binding with arguments is also known as 'curry'.
# Create a function bound to a given object (assigning `this`, and arguments,
# optionally). Binding with arguments is also known as **curry**.
_.bind: (func, obj) ->
args: _.rest arguments, 2
-> func.apply obj or root, args.concat arguments
@@ -395,7 +402,8 @@
args[0]
# ------------------------- Object Functions: ----------------------------
# Object Functions
# ----------------
# Retrieve the names of an object's properties.
_.keys: nativeKeys or (obj) ->
@@ -414,9 +422,10 @@
# Extend a given object with all of the properties in a source object.
_.extend: (destination, source) ->
(destination[key]: val) for key, val of source
destination
_.extend: (obj) ->
for source in _.rest(arguments)
(obj[key]: val) for key, val of source
obj
# Create a (shallow-cloned) duplicate of an object.
@@ -443,7 +452,7 @@
return true if `a == b`
# One is falsy and the other truthy.
return false if (!a and b) or (a and !b)
# One of them implements an isEqual()?
# One of them implements an `isEqual()`?
return a.isEqual(b) if a.isEqual
# Check dates' integer values.
return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
@@ -464,7 +473,7 @@
# Different object sizes?
return false if aKeys.length isnt bKeys.length
# Recursive comparison of contents.
(return false) for key, val of a when !_.isEqual(val, b[key])
(return false) for key, val of a when !(key in b) or !_.isEqual(val, b[key])
true
@@ -480,12 +489,11 @@
# Is a given value an array?
_.isArray: nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift)
_.isArray: nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift and not obj.callee)
# Is a given variable an arguments object?
_.isArguments: (obj) -> obj and _.isNumber(obj.length) and not obj.concat and
not obj.substr and not obj.apply and not propertyIsEnumerable.call(obj, 'length')
_.isArguments: (obj) -> obj and obj.callee
# Is the given value a function?
@@ -512,8 +520,8 @@
_.isRegExp: (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
# Is the given value NaN -- this one is interesting. NaN != NaN, and
# isNaN(undefined) == true, so we make sure it's a number first.
# Is the given value NaN -- this one is interesting. `NaN != NaN`, and
# `isNaN(undefined) == true`, so we make sure it's a number first.
_.isNaN: (obj) -> _.isNumber(obj) and window.isNaN(obj)
@@ -525,9 +533,10 @@
_.isUndefined: (obj) -> typeof obj is 'undefined'
# -------------------------- Utility Functions: --------------------------
# Utility Functions
# -----------------
# Run Underscore.js in noConflict mode, returning the '_' variable to its
# Run Underscore.js in noConflict mode, returning the `_` variable to its
# previous owner. Returns a reference to the Underscore object.
_.noConflict: ->
root._: previousUnderscore
@@ -538,7 +547,7 @@
_.identity: (value) -> value
# Run a function n times.
# Run a function `n` times.
_.times: (n, iterator, context) ->
iterator.call(context, i) for i in [0...n]
@@ -561,7 +570,7 @@
(prefix or '') + idCounter++
# By default, Underscore uses ERB-style template delimiters, change the
# By default, Underscore uses **ERB**-style template delimiters, change the
# following template settings to use alternative delimiters.
_.templateSettings: {
start: '<%'
@@ -570,9 +579,9 @@
}
# JavaScript templating a-la ERB, pilfered from John Resig's
# "Secrets of the JavaScript Ninja", page 83.
# Single-quote fix from Rick Strahl's version.
# JavaScript templating a-la **ERB**, pilfered from John Resig's
# *Secrets of the JavaScript Ninja*, page 83.
# Single-quote fix from Rick Strahl.
_.template: (str, data) ->
c: _.templateSettings
endMatch: new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g")
@@ -590,7 +599,8 @@
if data then fn(data) else fn
# ------------------------------- Aliases ----------------------------------
# Aliases
# -------
_.forEach: _.each
_.foldl: _.inject: _.reduce
@@ -603,7 +613,8 @@
_.methods: _.functions
# ------------------------ Setup the OOP Wrapper: --------------------------
# Setup the OOP Wrapper
# ---------------------
# If Underscore is called as a function, it returns a wrapped object that
# can be used OO-style. This wrapper holds altered versions of all the

View File

@@ -1,12 +1,13 @@
# Contributed by Jason Huggins
sys: require 'sys'
http: require 'http'
server: http.createServer (req, res) ->
res.sendHeader 200, {'Content-Type': 'text/plain'}
res.writeHeader 200, {'Content-Type': 'text/plain'}
res.write 'Hello, World!'
res.close()
res.end()
server.listen 3000
puts "Server running at http://localhost:3000/"
sys.puts "Server running at http://localhost:3000/"

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>comments</string>
<key>scope</key>
<string>source.coffee</string>
<key>settings</key>
<dict>
<key>shellVariables</key>
<array>
<dict>
<key>name</key>
<string>TM_COMMENT_START</string>
<key>value</key>
<string># </string>
</dict>
</array>
</dict>
<key>uuid</key>
<string>0A92C6F6-4D73-4859-B38C-4CC19CBC191F</string>
</dict>
</plist>

View File

@@ -1,361 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>comment</key>
<string>CoffeeScript Syntax: version 1</string>
<key>fileTypes</key>
<array>
<string>coffee</string>
</array>
<key>name</key>
<string>CoffeeScript</string>
<key>foldingStartMarker</key>
<string>^.*[:=] \{[^\}]*$</string>
<key>foldingStopMarker</key>
<string>\s*\}</string>
<key>patterns</key>
<array>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.parameter.function.coffee</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>variable.parameter.function.coffee</string>
</dict>
<key>4</key>
<dict>
<key>name</key>
<string>variable.parameter.function.coffee</string>
</dict>
<key>5</key>
<dict>
<key>name</key>
<string>storage.type.function.coffee</string>
</dict>
</dict>
<key>comment</key>
<string>match stuff like: a -&gt; … </string>
<key>match</key>
<string>(\()([a-zA-Z0-9_?.$]*(,\s*[a-zA-Z0-9_?.$]+)*)(\))\s*((=|-)&gt;)</string>
<key>name</key>
<string>meta.inline.function.coffee</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>keyword.operator.new.coffee</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>entity.name.type.instance.coffee</string>
</dict>
</dict>
<key>match</key>
<string>(new)\s+(\w+(?:\.\w*)?)</string>
<key>name</key>
<string>meta.class.instance.constructor</string>
</dict>
<dict>
<key>match</key>
<string>\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?))\b</string>
<key>name</key>
<string>constant.numeric.coffee</string>
</dict>
<dict>
<key>match</key>
<string>(@)([a-zA-Z_$]\w*)?</string>
<key>name</key>
<string>variable.other.readwrite.instance.coffee</string>
</dict>
<dict>
<key>name</key>
<string>string.quoted.heredoc.coffee</string>
<key>begin</key>
<string>("""|''')</string>
<key>end</key>
<string>("""|''')</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
</dict>
<dict>
<key>begin</key>
<string>'</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>end</key>
<string>'</string>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.single.coffee</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)</string>
<key>name</key>
<string>constant.character.escape.coffee</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>"</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>end</key>
<string>"</string>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.double.coffee</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)</string>
<key>name</key>
<string>constant.character.escape.coffee</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>`</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>end</key>
<string>`</string>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.script.coffee</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)</string>
<key>name</key>
<string>constant.character.escape.coffee</string>
</dict>
</array>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.coffee</string>
</dict>
</dict>
<key>match</key>
<string>(#).*$\n?</string>
<key>name</key>
<string>comment.line.coffee</string>
</dict>
<dict>
<key>begin</key>
<string>(?&lt;=[=(:]|^|return)\s*(/)(?![/*+{}?])</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>end</key>
<string>(/)[igm]*</string>
<key>endCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
<key>name</key>
<string>string.regexp.coffee</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\.</string>
<key>name</key>
<string>constant.character.escape.coffee</string>
</dict>
</array>
</dict>
<dict>
<key>match</key>
<string>\b(break|by|catch|continue|else|finally|for|in|of|if|return|switch|then|throw|try|unless|when|while)\b</string>
<key>name</key>
<string>keyword.control.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b([a-zA-Z$_](\w|\$|:|\.)*\s*(?=\:))</string>
<key>name</key>
<string>variable.assignment.coffee</string>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>entity.name.function.coffee</string>
</dict>
</dict>
</dict>
<dict>
<key>match</key>
<string>\b(true|on|yes)\b</string>
<key>name</key>
<string>constant.language.boolean.true.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(false|off|no)\b</string>
<key>name</key>
<string>constant.language.boolean.false.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\bnull\b</string>
<key>name</key>
<string>constant.language.null.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(super|this|extends|class)\b</string>
<key>name</key>
<string>variable.language.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(debugger|\\)\b</string>
<key>name</key>
<string>keyword.other.coffee</string>
</dict>
<dict>
<key>match</key>
<string>(=|-)&gt;</string>
<key>name</key>
<string>storage.type.function.coffee</string>
</dict>
<dict>
<key>match</key>
<string>!|%|&amp;|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\b(instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
<key>name</key>
<string>keyword.operator.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(Infinity|NaN|undefined)\b</string>
<key>name</key>
<string>constant.language.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\;</string>
<key>name</key>
<string>punctuation.terminator.statement.coffee</string>
</dict>
<dict>
<key>match</key>
<string>,[ |\t]*</string>
<key>name</key>
<string>meta.delimiter.object.comma.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\.</string>
<key>name</key>
<string>meta.delimiter.method.period.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\{|\}</string>
<key>name</key>
<string>meta.brace.curly.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\(|\)</string>
<key>name</key>
<string>meta.brace.round.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\[|\]</string>
<key>name</key>
<string>meta.brace.square.coffee</string>
</dict>
</array>
<key>scopeName</key>
<string>source.coffee</string>
<key>uuid</key>
<string>5B520980-A7D5-4E10-8582-1A4C889A8DE5</string>
</dict>
</plist>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>CoffeeScript</string>
<key>uuid</key>
<string>A46E4382-F1AC-405B-8F22-65FF470F34D7</string>
</dict>
</plist>

View File

@@ -5,24 +5,3 @@ CoffeeScript compiler. To use it in the browser, include the script after any
inline script tags of type "text/coffeescript" on the page. It will compile
and evaluate all of the scripts in order.
This folder also includes rough cuts of CoffeeScript syntax highlighters for
TextMate and Vim. Improvements to their lexing ability are always welcome.
To install the TextMate bundle, drop it into:
~/Library/Application Support/TextMate/Bundles
To install the Vim highlighter, copy "coffee.vim" into the "syntax" directory of
your vim72, and enable it in either of the following two ways:
* Manually, by running `:set syntax=coffee`
* Or automatically, by creating a "filetype.vim" file within "~/.vim", which
contains something along these lines:
if exists("did_load_filetypes")
finish
end
augroup filetypedetect
au! BufRead,BufNewFile *.coffee setfiletype coffee
augroup END

File diff suppressed because one or more lines are too long

View File

@@ -1,117 +0,0 @@
" Vim syntax file
" Language: CoffeeScript
" Maintainer: Jeff Olson <olson.jeffery@gmail.com>
" URL: http://github.com/olsonjeffery
" Changes: (jro) initial port from javascript
" Last Change: 2006 Jun 19
" Adaptation of javascript.vim syntax file (distro'd w/ vim72),
" maintained by Claudio Fleiner <claudio@fleiner.com>
" with updates from Scott Shattuck (ss) <ss@technicalpursuit.com>
if !exists("main_syntax")
if version < 600
syntax clear
elseif exists("b:current_syntax")
finish
endif
let main_syntax = 'coffee'
endif
syn case ignore
syn match coffeeLineComment "#.*" contains=@Spell,CoffeeCommentTodo
syn match coffeeSpecial "\\\d\d\d\|\\."
syn region coffeeStringD start=+"+ skip=+\\\\\|\\"+ end=+"\|$+ contains=coffeeSpecial,@htmlPreproc
syn region coffeeStringS start=+'+ skip=+\\\\\|\\'+ end=+'\|$+ contains=coffeeSpecial,@htmlPreproc
syn match coffeeSpecialCharacter "'\\.'"
syn match coffeeNumber "-\=\<\d\+L\=\>\|0[xX][0-9a-fA-F]\+\>"
syn region coffeeRegexpString start=+/[^/*]+me=e-1 skip=+\\\\\|\\/+ end=+/[gi]\{0,2\}\s*$+ end=+/[gi]\{0,2\}\s*[;.,)\]}]+me=e-1 contains=@htmlPreproc oneline
syn match coffeeFunctionParams "([^)]*)\s*->"
syn match coffeeBindFunctionParams "([^)]*)\s*=>"
syn match coffeePrototypeAccess "::"
syn match coffeeBindFunction "=[1]>[1]"
syn match coffeeFunction "->"
syn keyword coffeeExtends extends
syn keyword coffeeConditional if else switch then not
syn keyword coffeeRepeat while for in of
syn keyword coffeeBranch break continue
syn keyword coffeeOperator delete instanceof typeof
syn keyword coffeeType Array Boolean Date Function Number Object String RegExp
syn keyword coffeeStatement return with
syn keyword coffeeBoolean true false
syn keyword coffeeNull null undefined
syn keyword coffeeIdentifier arguments this var
syn keyword coffeeLabel case default
syn keyword coffeeException try catch finally throw
syn keyword coffeeMessage alert confirm prompt status
syn keyword coffeeGlobal self window top parent
syn keyword coffeeMember document event location
syn keyword coffeeDeprecated escape unescape
syn keyword coffeeReserved abstract boolean byte char class const debugger double enum export final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile
syn sync fromstart
syn sync maxlines=100
if main_syntax == "coffee"
syn sync ccomment coffeeComment
endif
" Define the default highlighting.
" For version 5.7 and earlier: only when not done already
" For version 5.8 and later: only when an item doesn't have highlighting yet
if version >= 508 || !exists("did_coffee_syn_inits")
if version < 508
let did_coffee_syn_inits = 1
command -nargs=+ HiLink hi link <args>
else
command -nargs=+ HiLink hi def link <args>
endif
HiLink coffeePrototypeAccess Keyword
HiLink coffeeExtends Keyword
HiLink coffeeLineComment Comment
HiLink coffeeSpecial Special
HiLink coffeeStringS String
HiLink coffeeStringD String
HiLink coffeeCharacter Character
HiLink coffeeSpecialCharacter coffeeSpecial
HiLink coffeeNumber coffeeValue
HiLink coffeeConditional Conditional
HiLink coffeeRepeat Repeat
HiLink coffeeBranch Conditional
HiLink coffeeOperator Operator
HiLink coffeeType Type
HiLink coffeeStatement Statement
HiLink coffeeBindFunctionParams Function
HiLink coffeeFunctionParams Function
HiLink coffeeFunction Function
HiLink coffeeBindFunction Function
HiLink coffeeBraces Function
HiLink coffeeError Error
HiLink coffeeScrParenError coffeeError
HiLink coffeeNull Keyword
HiLink coffeeBoolean Boolean
HiLink coffeeRegexpString String
HiLink coffeeIdentifier Identifier
HiLink coffeeLabel Label
HiLink coffeeException Exception
HiLink coffeeMessage Keyword
HiLink coffeeGlobal Keyword
HiLink coffeeMember Keyword
HiLink coffeeDeprecated Exception
HiLink coffeeReserved Keyword
HiLink coffeeDebug Debug
HiLink coffeeConstant Label
delcommand HiLink
endif
let b:current_syntax = "coffee"
if main_syntax == 'coffee'
unlet main_syntax
endif
" vim: ts=8

File diff suppressed because it is too large Load Diff

View File

@@ -1,86 +1,106 @@
(function(){
var coffee, fs, no_such_task, oparse, options, optparse, path, print_tasks, switches, tasks;
var CoffeeScript, fs, helpers, no_such_task, oparse, options, optparse, path, print_tasks, switches, tasks;
var __hasProp = Object.prototype.hasOwnProperty;
// `cake` is a simplified version of Make (Rake, Jake) for CoffeeScript.
// You define tasks with names and descriptions in a Cakefile, and can call them
// from the command line, or invoke them from other tasks.
// `cake` is a simplified version of [Make](http://www.gnu.org/software/make/)
// ([Rake](http://rake.rubyforge.org/), [Jake](http://github.com/280north/jake))
// for CoffeeScript. You define tasks with names and descriptions in a Cakefile,
// and can call them from the command line, or invoke them from other tasks.
// Running `cake` with no arguments will print out a list of all the tasks in the
// current directory's Cakefile.
// External dependencies.
fs = require('fs');
path = require('path');
coffee = require('coffee-script');
optparse = require('optparse');
helpers = require('./helpers').helpers;
optparse = require('./optparse');
CoffeeScript = require('./coffee-script');
// Keep track of the list of defined tasks, the accepted options, and so on.
tasks = {};
options = {};
switches = [];
oparse = null;
// Mixin the top-level Cake functions for Cakefiles to use.
process.mixin({
// Define a task with a name, a description, and the action itself.
task: function task(name, description, action) {
return tasks[name] = {
// Mixin the top-level Cake functions for Cakefiles to use directly.
helpers.extend(global, {
// Define a Cake task with a short name, an optional sentence description,
// and the function to run as the action itself.
task: function(name, description, action) {
var _a;
if (!(action)) {
_a = [description, action];
action = _a[0];
description = _a[1];
}
tasks[name] = {
name: name,
description: description,
action: action
};
return tasks[name];
},
// Define an option that the Cakefile accepts.
option: function option(letter, flag, description) {
// Define an option that the Cakefile accepts. The parsed options hash,
// containing all of the command-line options passed, will be made available
// as the first argument to the action.
option: function(letter, flag, description) {
return switches.push([letter, flag, description]);
},
// Invoke another task in the Cakefile.
invoke: function invoke(name) {
// Invoke another task in the current Cakefile.
invoke: function(name) {
if (!(tasks[name])) {
no_such_task(name);
}
return tasks[name].action(options);
}
});
// Running `cake` runs the tasks you pass asynchronously (node-style), or
// prints them out, with no arguments.
exports.run = function run() {
// Run `cake`. Executes all of the tasks you pass, in order. Note that Node's
// asynchrony may cause tasks to execute in a different order than you'd expect.
// If no tasks are passed, print the help screen.
exports.run = function() {
return path.exists('Cakefile', function(exists) {
var _a, _b, _c, _d, arg, args;
if (!(exists)) {
throw new Error('Cakefile not found in ' + process.cwd());
throw new Error(("Cakefile not found in " + (process.cwd())));
}
args = process.ARGV.slice(2, process.ARGV.length);
eval(coffee.compile(fs.readFileSync('Cakefile')));
args = process.argv.slice(2, process.argv.length);
CoffeeScript.run(fs.readFileSync('Cakefile').toString(), {
source: 'Cakefile'
});
oparse = new optparse.OptionParser(switches);
if (!(args.length)) {
return print_tasks();
}
options = oparse.parse(args);
_a = []; _b = options.arguments;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
arg = _b[_c];
_a = []; _c = options.arguments;
for (_b = 0, _d = _c.length; _b < _d; _b++) {
arg = _c[_b];
_a.push(invoke(arg));
}
return _a;
});
};
// Display the list of Cake tasks.
print_tasks = function print_tasks() {
var _a, _b, _c, _d, _e, _f, i, name, spaces, task;
// Display the list of Cake tasks in a format similar to `rake -T`
print_tasks = function() {
var _a, _b, _c, _d, desc, i, name, spaces, task;
puts('');
_a = tasks;
for (name in _a) { if (__hasProp.call(_a, name)) {
task = _a[name];
spaces = 20 - name.length;
spaces = spaces > 0 ? (function() {
_b = []; _e = 0; _f = spaces;
for (_d = 0, i = _e; (_e <= _f ? i <= _f : i >= _f); (_e <= _f ? i += 1 : i -= 1), _d++) {
_b = []; _c = 0; _d = spaces;
for (i = _c; (_c <= _d ? i <= _d : i >= _d); (_c <= _d ? i += 1 : i -= 1)) {
_b.push(' ');
}
return _b;
}).call(this).join('') : '';
puts("cake " + name + spaces + ' # ' + task.description);
})().join('') : '';
desc = task.description ? ("# " + task.description) : '';
puts(("cake " + name + spaces + " " + desc));
}}
if (switches.length) {
return puts('\n' + oparse.help() + '\n');
return puts(oparse.help());
}
};
// Print an error and exit when attempting to all an undefined task.
no_such_task = function no_such_task(task) {
process.stdio.writeError('No such task: "' + task + '"\n');
no_such_task = function(task) {
puts(("No such task: \"" + task + "\""));
return process.exit(1);
};
})();

View File

@@ -1,19 +1,74 @@
(function(){
var lexer, parser, path, process_scripts;
// Set up for both the browser and the server.
var Lexer, compile, helpers, lexer, parser, path, process_scripts;
// CoffeeScript can be used both on the server, as a command-line compiler based
// on Node.js/V8, or to run CoffeeScripts directly in the browser. This module
// contains the main entry functions for tokenzing, parsing, and compiling source
// CoffeeScript into JavaScript.
// If included on a webpage, it will automatically sniff out, compile, and
// execute all scripts present in `text/coffeescript` tags.
// Set up dependencies correctly for both the server and the browser.
if ((typeof process !== "undefined" && process !== null)) {
process.mixin(require('nodes'));
path = require('path');
lexer = new (require('lexer').Lexer)();
parser = require('parser').parser;
Lexer = require('./lexer').Lexer;
parser = require('./parser').parser;
helpers = require('./helpers').helpers;
helpers.extend(global, require('./nodes'));
require.registerExtension ? require.registerExtension('.coffee', function(content) {
return compile(content);
}) : null;
} else {
lexer = new Lexer();
parser = exports.parser;
this.exports = (this.CoffeeScript = {});
Lexer = this.Lexer;
parser = this.parser;
helpers = this.helpers;
}
// Thin wrapper for Jison compatibility around the real lexer.
// The current CoffeeScript version number.
exports.VERSION = '0.6.2';
// Instantiate a Lexer for our use here.
lexer = new Lexer();
// Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison
// compiler.
exports.compile = (compile = function(code, options) {
options = options || {};
try {
return (parser.parse(lexer.tokenize(code))).compile(options);
} catch (err) {
if (options.source) {
err.message = ("In " + options.source + ", " + err.message);
}
throw err;
}
});
// Tokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens = function(code) {
return lexer.tokenize(code);
};
// Tokenize and parse a string of CoffeeScript code, and return the AST. You can
// then compile it by calling `.compile()` on the root, or traverse it by using
// `.traverse()` with a callback.
exports.nodes = function(code) {
return parser.parse(lexer.tokenize(code));
};
// Compile and execute a string of CoffeeScript (on the server), correctly
// setting `__filename`, `__dirname`, and relative `require()`.
exports.run = (function(code, options) {
var __dirname, __filename;
module.filename = (__filename = options.source);
__dirname = path.dirname(__filename);
return eval(exports.compile(code, options));
});
// Extend CoffeeScript with a custom language extension. It should hook in to
// the **Lexer** (as a peer of any of the lexer's tokenizing methods), and
// push a token on to the stack that contains a **Node** as the value (as a
// peer of the nodes in [nodes.coffee](nodes.html)).
exports.extend = function(func) {
return Lexer.extensions.push(func);
};
// The real Lexer produces a generic stream of tokens. This object provides a
// thin wrapper around it, compatible with the Jison API. We can then pass it
// directly as a "Jison lexer".
parser.lexer = {
lex: function lex() {
lex: function() {
var token;
token = this.tokens[this.pos] || [""];
this.pos += 1;
@@ -21,41 +76,29 @@
this.yytext = token[1];
return token[0];
},
setInput: function setInput(tokens) {
setInput: function(tokens) {
this.tokens = tokens;
return this.pos = 0;
this.pos = 0;
return this.pos;
},
upcomingInput: function upcomingInput() {
upcomingInput: function() {
return "";
},
showPosition: function showPosition() {
showPosition: function() {
return this.pos;
}
};
exports.VERSION = '0.5.4';
// Compile CoffeeScript to JavaScript, using the Coffee/Jison compiler.
exports.compile = function compile(code, options) {
return (parser.parse(lexer.tokenize(code))).compile(options);
};
// Just the tokens.
exports.tokens = function tokens(code) {
return lexer.tokenize(code);
};
// Just the nodes.
exports.nodes = function nodes(code) {
return parser.parse(lexer.tokenize(code));
};
// Activate CoffeeScript in the browser by having it compile and eval
// all script tags with a content-type of text/coffeescript.
// Activate CoffeeScript in the browser by having it compile and evaluate
// all script tags with a content-type of `text/coffeescript`. This happens
// on page load. Unfortunately, the text contents of remote scripts cannot be
// accessed from the browser, so only inline script tags will work.
if ((typeof document !== "undefined" && document !== null) && document.getElementsByTagName) {
process_scripts = function process_scripts() {
process_scripts = function() {
var _a, _b, _c, _d, tag;
_a = []; _b = document.getElementsByTagName('script');
for (_c = 0, _d = _b.length; _c < _d; _c++) {
tag = _b[_c];
if (tag.type === 'text/coffeescript') {
_a.push(eval(exports.compile(tag.innerHTML)));
}
_a = []; _c = document.getElementsByTagName('script');
for (_b = 0, _d = _c.length; _b < _d; _b++) {
tag = _c[_b];
tag.type === 'text/coffeescript' ? _a.push(eval(exports.compile(tag.innerHTML))) : null;
}
return _a;
};

247
lib/command.js Normal file
View File

@@ -0,0 +1,247 @@
(function(){
var BANNER, CoffeeScript, SWITCHES, _a, compile_options, compile_script, compile_scripts, compile_stdio, exec, fs, lint, option_parser, options, optparse, parse_options, path, print_tokens, sources, spawn, usage, version, watch, write_js;
// The `coffee` utility. Handles command-line compilation of CoffeeScript
// into various forms: saved into `.js` files or printed to stdout, piped to
// [JSLint](http://javascriptlint.com/) or recompiled every time the source is
// saved, printed as a token stream or as the syntax tree, or launch an
// interactive REPL.
// External dependencies.
fs = require('fs');
path = require('path');
optparse = require('./optparse');
CoffeeScript = require('./coffee-script');
_a = require('child_process');
spawn = _a.spawn;
exec = _a.exec;
// The help banner that is printed when `coffee` is called without arguments.
BANNER = 'coffee compiles CoffeeScript source files into JavaScript.\n\nUsage:\n coffee path/to/script.coffee';
// The list of all the valid option flags that `coffee` knows how to handle.
SWITCHES = [['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JSLint'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-e', '--eval', 'compile a string from the command line'], ['--no-wrap', 'compile without the top-level function wrapper'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-n', '--nodes', 'print the parse tree that Jison produces'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']];
// Top-level objects shared by all the functions.
options = {};
sources = [];
option_parser = null;
// Run `coffee` by parsing passed options and determining what action to take.
// Many flags cause us to divert before compiling anything. Flags passed after
// `--` will be passed verbatim to your script as arguments in `process.argv`
exports.run = function() {
var flags, separator;
parse_options();
if (options.help) {
return usage();
}
if (options.version) {
return version();
}
if (options.interactive) {
return require('./repl');
}
if (options.stdio) {
return compile_stdio();
}
if (options.eval) {
return compile_script('console', sources[0]);
}
if (!(sources.length)) {
return usage();
}
separator = sources.indexOf('--');
flags = [];
if (separator >= 0) {
flags = sources.slice((separator + 1), sources.length);
sources = sources.slice(0, separator);
}
process.ARGV = (process.argv = flags);
return compile_scripts();
};
// Asynchronously read in each CoffeeScript in a list of source files and
// compile them. If a directory is passed, recursively compile all source
// files in it and all subdirectories.
compile_scripts = function() {
var _b, _c, _d, _e, base, compile, source;
_b = []; _d = sources;
for (_c = 0, _e = _d.length; _c < _e; _c++) {
source = _d[_c];
_b.push((function() {
base = source;
compile = function(source) {
return path.exists(source, function(exists) {
if (!(exists)) {
throw new Error(("File not found: " + source));
}
return fs.stat(source, function(err, stats) {
if (stats.isDirectory()) {
return fs.readdir(source, function(err, files) {
var _f, _g, _h, _i, file;
_f = []; _h = files;
for (_g = 0, _i = _h.length; _g < _i; _g++) {
file = _h[_g];
_f.push(compile(path.join(source, file)));
}
return _f;
});
} else if (path.extname(source) === '.coffee') {
fs.readFile(source, function(err, code) {
return compile_script(source, code.toString(), base);
});
if (options.watch) {
return watch(source, base);
}
}
});
});
};
return compile(source);
})());
}
return _b;
};
// 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.
compile_script = function(source, code, base) {
var code_opts, js, o;
o = options;
code_opts = compile_options(source);
try {
if (o.tokens) {
return print_tokens(CoffeeScript.tokens(code));
} else if (o.nodes) {
return puts(CoffeeScript.nodes(code).toString());
} else if (o.run) {
return CoffeeScript.run(code, code_opts);
} else {
js = CoffeeScript.compile(code, code_opts);
if (o.print) {
return print(js);
} else if (o.compile) {
return write_js(source, js, base);
} else if (o.lint) {
return lint(js);
}
}
} catch (err) {
if (o.watch) {
return puts(err.message);
} else {
throw err;
}
}
};
// Attach the appropriate listeners to compile scripts incoming over **stdin**,
// and write them back to **stdout**.
compile_stdio = function() {
var code, stdin;
code = '';
stdin = process.openStdin();
stdin.addListener('data', function(buffer) {
if (buffer) {
return code += buffer.toString();
}
});
return stdin.addListener('end', function() {
return compile_script('stdio', code);
});
};
// Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every
// time the file is updated. May be used in combination with other options,
// such as `--lint` or `--print`.
watch = function(source, base) {
return fs.watchFile(source, {
persistent: true,
interval: 500
}, function(curr, prev) {
if (curr.mtime.getTime() === prev.mtime.getTime()) {
return null;
}
if (options.compile) {
puts(("Compiled " + source));
}
return fs.readFile(source, function(err, code) {
return compile_script(source, code.toString(), base);
});
});
};
// Write out a JavaScript source file with the compiled code. By default, files
// are written out in `cwd` as `.js` files with the same name, but the output
// directory can be customized with `--output`.
write_js = function(source, js, base) {
var base_dir, compile, dir, filename, js_path, src_dir;
filename = path.basename(source, path.extname(source)) + '.js';
src_dir = path.dirname(source);
base_dir = src_dir.substring(base.length);
dir = options.output ? path.join(options.output, base_dir) : src_dir;
js_path = path.join(dir, filename);
compile = function() {
return fs.writeFile(js_path, js);
};
return path.exists(dir, function(exists) {
if (exists) {
return compile();
} else {
return exec(("mkdir -p " + dir), compile);
}
});
};
// Pipe compiled JS through JSLint (requires a working `jsl` command), printing
// any errors or warnings that arise.
lint = function(js) {
var jsl, print_it;
print_it = function(buffer) {
return print(buffer.toString());
};
jsl = spawn('jsl', ['-nologo', '-stdin']);
jsl.stdout.addListener('data', print_it);
jsl.stderr.addListener('data', print_it);
jsl.stdin.write(js);
return jsl.stdin.end();
};
// Pretty-print a stream of tokens.
print_tokens = function(tokens) {
var _b, _c, _d, _e, _f, strings, tag, token, value;
strings = (function() {
_b = []; _d = tokens;
for (_c = 0, _e = _d.length; _c < _e; _c++) {
token = _d[_c];
_b.push((function() {
_f = [token[0], token[1].toString().replace(/\n/, '\\n')];
tag = _f[0];
value = _f[1];
return "[" + tag + " " + value + "]";
})());
}
return _b;
})();
return puts(strings.join(' '));
};
// Use the [OptionParser module](optparse.html) to extract all options from
// `process.argv` that are specified in `SWITCHES`.
parse_options = function() {
var o;
option_parser = new optparse.OptionParser(SWITCHES, BANNER);
o = (options = option_parser.parse(process.argv));
options.run = !(o.compile || o.print || o.lint);
options.print = !!(o.print || (o.eval || o.stdio && o.compile));
sources = options.arguments.slice(2, options.arguments.length);
return sources;
};
// The compile-time options to pass to the CoffeeScript compiler.
compile_options = function(source) {
var o;
o = {
source: source
};
o['no_wrap'] = options['no-wrap'];
return o;
};
// Print the `--help` usage message and exit.
usage = function() {
puts(option_parser.help());
return process.exit(0);
};
// Print the `--version` message and exit.
version = function() {
puts(("CoffeeScript version " + CoffeeScript.VERSION));
return process.exit(0);
};
})();

View File

@@ -1,198 +0,0 @@
(function(){
var BANNER, CoffeeScript, SWITCHES, compile_options, compile_script, compile_scripts, compile_stdio, fs, lint, option_parser, options, optparse, parse_options, path, print_tokens, sources, usage, version, watch_scripts, write_js;
fs = require('fs');
path = require('path');
optparse = require('optparse');
CoffeeScript = require('coffee-script');
BANNER = "coffee compiles CoffeeScript source files into JavaScript.\n\nUsage:\n coffee path/to/script.coffee";
SWITCHES = [['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JSLint'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-e', '--eval', 'compile a string from the command line'], ['--no-wrap', 'compile without the top-level function wrapper'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-n', '--nodes', 'print the parse tree that Jison produces'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']];
options = {};
sources = [];
option_parser = null;
// The CommandLine handles all of the functionality of the `coffee` utility.
exports.run = function run() {
var flags, separator;
parse_options();
if (options.help) {
return usage();
}
if (options.version) {
return version();
}
if (options.interactive) {
return require('repl');
}
if (options.stdio) {
return compile_stdio();
}
if (options.eval) {
return compile_script('unknown', sources[0]);
}
if (!(sources.length)) {
return usage();
}
separator = sources.indexOf('--');
flags = [];
if (separator >= 0) {
flags = sources.slice((separator + 1), sources.length);
sources = sources.slice(0, separator);
}
process.ARGV = flags;
if (options.watch) {
watch_scripts();
}
compile_scripts();
return this;
};
// The "--help" usage message.
usage = function usage() {
puts('\n' + option_parser.help() + '\n');
return process.exit(0);
};
// The "--version" message.
version = function version() {
puts("CoffeeScript version " + CoffeeScript.VERSION);
return process.exit(0);
};
// Compiles the source CoffeeScript, returning the desired JavaScript, tokens,
// or JSLint results.
compile_scripts = function compile_scripts() {
var _a, _b, _c, _d, compile, source;
compile = function compile(source) {
return path.exists(source, function(exists) {
if (!(exists)) {
throw new Error('File not found: ' + source);
}
return fs.readFile(source, function(err, code) {
return compile_script(source, code);
});
});
};
_a = []; _b = sources;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
source = _b[_c];
_a.push(compile(source));
}
return _a;
};
// Compile a single source script, containing the given code, according to the
// requested options. Both compile_scripts and watch_scripts share this method.
compile_script = function compile_script(source, code) {
var __dirname, __filename, js, o;
o = options;
try {
if (o.tokens) {
return print_tokens(CoffeeScript.tokens(code));
} else if (o.nodes) {
return puts(CoffeeScript.nodes(code).toString());
} else {
js = CoffeeScript.compile(code, compile_options());
if (o.compile) {
return write_js(source, js);
} else if (o.lint) {
return lint(js);
} else if (o.print || o.eval) {
return print(js);
} else {
__filename = source;
__dirname = path.dirname(source);
return eval(js);
}
}
} catch (err) {
if (o.watch) {
return puts(err.message);
} else {
throw err;
}
}
};
// Listen for and compile scripts over stdio.
compile_stdio = function compile_stdio() {
var code;
code = '';
process.stdio.open();
process.stdio.addListener('data', function(string) {
if (string) {
return code += string;
}
});
return process.stdio.addListener('close', function() {
return process.stdio.write(CoffeeScript.compile(code, compile_options()));
});
};
// Watch a list of source CoffeeScript files, recompiling them every time the
// files are updated.
watch_scripts = function watch_scripts() {
var _a, _b, _c, _d, source, watch;
watch = function watch(source) {
return fs.watchFile(source, {
persistent: true,
interval: 500
}, function(curr, prev) {
if (curr.mtime.getTime() === prev.mtime.getTime()) {
return null;
}
return fs.readFile(source, function(err, code) {
return compile_script(source, code);
});
});
};
_a = []; _b = sources;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
source = _b[_c];
_a.push(watch(source));
}
return _a;
};
// Write out a JavaScript source file with the compiled code.
write_js = function write_js(source, js) {
var dir, filename, js_path;
filename = path.basename(source, path.extname(source)) + '.js';
dir = options.output || path.dirname(source);
js_path = path.join(dir, filename);
return fs.writeFile(js_path, js);
};
// Pipe compiled JS through JSLint (requires a working 'jsl' command).
lint = function lint(js) {
var jsl;
jsl = process.createChildProcess('jsl', ['-nologo', '-stdin']);
jsl.addListener('output', function(result) {
if (result) {
return puts(result.replace(/\n/g, ''));
}
});
jsl.addListener('error', function(result) {
if (result) {
return puts(result);
}
});
jsl.write(js);
return jsl.close();
};
// Pretty-print a token stream.
print_tokens = function print_tokens(tokens) {
var _a, _b, _c, _d, strings, token;
strings = (function() {
_a = []; _b = tokens;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
token = _b[_c];
_a.push('[' + token[0] + ' ' + token[1].toString().replace(/\n/, '\\n') + ']');
}
return _a;
}).call(this);
return puts(strings.join(' '));
};
// Use OptionParser for all the options.
parse_options = function parse_options() {
option_parser = new optparse.OptionParser(SWITCHES, BANNER);
options = option_parser.parse(process.ARGV);
return sources = options.arguments.slice(2, options.arguments.length);
};
// The options to pass to the CoffeeScript compiler.
compile_options = function compile_options() {
return options['no-wrap'] ? {
no_wrap: true
} : {};
};
})();

File diff suppressed because it is too large Load Diff

158
lib/helpers.js Normal file
View File

@@ -0,0 +1,158 @@
(function(){
var balanced_string, compact, count, del, extend, flatten, helpers, include, index_of, merge, starts;
var __hasProp = Object.prototype.hasOwnProperty;
// This file contains the common helper functions that we'd like to share among
// the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten
// arrays, count characters, that sort of thing.
// Set up exported variables for both **Node.js** and the browser.
if (!((typeof process !== "undefined" && process !== null))) {
this.exports = this;
}
helpers = (exports.helpers = {});
// Cross-browser indexOf, so that IE can join the party.
helpers.index_of = (index_of = function(array, item, from) {
var _a, _b, index, other;
if (array.indexOf) {
return array.indexOf(item, from);
}
_a = array;
for (index = 0, _b = _a.length; index < _b; index++) {
other = _a[index];
if (other === item && (!from || (from <= index))) {
return index;
}
}
return -1;
});
// Does a list include a value?
helpers.include = (include = function(list, value) {
return index_of(list, value) >= 0;
});
// Peek at the beginning of a given string to see if it matches a sequence.
helpers.starts = (starts = function(string, literal, start) {
return string.substring(start, (start || 0) + literal.length) === literal;
});
// Trim out all falsy values from an array.
helpers.compact = (compact = function(array) {
var _a, _b, _c, _d, item;
_a = []; _c = array;
for (_b = 0, _d = _c.length; _b < _d; _b++) {
item = _c[_b];
item ? _a.push(item) : null;
}
return _a;
});
// Count the number of occurences of a character in a string.
helpers.count = (count = function(string, letter) {
var num, pos;
num = 0;
pos = index_of(string, letter);
while (pos !== -1) {
num += 1;
pos = index_of(string, letter, pos + 1);
}
return num;
});
// Merge objects, returning a fresh copy with attributes from both sides.
// Used every time `BaseNode#compile` is called, to allow properties in the
// options hash to propagate down the tree without polluting other branches.
helpers.merge = (merge = function(options, overrides) {
var _a, _b, fresh, key, val;
fresh = {};
_a = options;
for (key in _a) { if (__hasProp.call(_a, key)) {
val = _a[key];
(fresh[key] = val);
}}
if (overrides) {
_b = overrides;
for (key in _b) { if (__hasProp.call(_b, key)) {
val = _b[key];
(fresh[key] = val);
}}
}
return fresh;
});
// Extend a source object with the properties of another object (shallow copy).
// We use this to simulate Node's deprecated `process.mixin`
helpers.extend = (extend = function(object, properties) {
var _a, _b, key, val;
_a = []; _b = properties;
for (key in _b) { if (__hasProp.call(_b, key)) {
val = _b[key];
_a.push((object[key] = val));
}}
return _a;
});
// Return a completely flattened version of an array. Handy for getting a
// list of `children` from the nodes.
helpers.flatten = (flatten = function(array) {
var _a, _b, _c, item, memo;
memo = [];
_b = array;
for (_a = 0, _c = _b.length; _a < _c; _a++) {
item = _b[_a];
item instanceof Array ? (memo = memo.concat(item)) : memo.push(item);
}
return memo;
});
// Delete a key from an object, returning the value. Useful when a node is
// looking for a particular method in an options hash.
helpers.del = (del = function(obj, key) {
var val;
val = obj[key];
delete obj[key];
return val;
});
// Matches a balanced group such as a single or double-quoted string. Pass in
// a series of delimiters, all of which must be nested correctly within the
// contents of the string. This method allows us to have strings within
// interpolations within strings, ad infinitum.
helpers.balanced_string = (balanced_string = function(str, delimited, options) {
var _a, _b, _c, _d, close, i, levels, open, pair, slash;
options = options || {};
slash = delimited[0][0] === '/';
levels = [];
i = 0;
while (i < str.length) {
if (levels.length && starts(str, '\\', i)) {
i += 1;
} else {
_b = delimited;
for (_a = 0, _c = _b.length; _a < _c; _a++) {
pair = _b[_a];
_d = pair;
open = _d[0];
close = _d[1];
if (levels.length && starts(str, close, i) && levels[levels.length - 1] === pair) {
levels.pop();
i += close.length - 1;
if (!(levels.length)) {
i += 1;
}
break;
} else if (starts(str, open, i)) {
levels.push(pair);
i += open.length - 1;
break;
}
}
}
if (!levels.length || slash && starts(str, '\n', i)) {
break;
}
i += 1;
}
if (levels.length) {
if (slash) {
return false;
}
throw new Error(("SyntaxError: Unterminated " + (levels.pop()[0]) + " starting on line " + (this.line + 1)));
}
if (!i) {
return false;
} else {
return str.substring(0, i);
}
});
})();

10
lib/index.js Normal file
View File

@@ -0,0 +1,10 @@
(function(){
var _a, key, val;
var __hasProp = Object.prototype.hasOwnProperty;
// Loader for CoffeeScript as a Node.js library.
_a = require('./coffee-script');
for (key in _a) { if (__hasProp.call(_a, key)) {
val = _a[key];
(exports[key] = val);
}}
})();

View File

@@ -1,5 +1,6 @@
(function(){
var ACCESSORS, ASSIGNMENT, BEFORE_WHEN, CALLABLE, CODE, COFFEE_KEYWORDS, COMMENT, COMMENT_CLEANER, HEREDOC, HEREDOC_INDENT, IDENTIFIER, JS, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, KEYWORDS, LAST_DENT, LAST_DENTS, Lexer, MULTILINER, MULTI_DENT, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RESERVED, Rewriter, STRING, STRING_NEWLINES, WHITESPACE, compact, count, include;
var ACCESSORS, ASSIGNMENT, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMMENT_CLEANER, CONVERSIONS, HALF_ASSIGNMENTS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, INTERPOLATION, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, KEYWORDS, LAST_DENT, LAST_DENTS, LINE_BREAK, Lexer, MULTILINER, MULTI_DENT, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, _a, _b, _c, balanced_string, compact, count, helpers, include, starts;
var __slice = Array.prototype.slice;
// The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
// matches against the beginning of the source code. When a match is found,
// a token is produced, we consume the match, and start again. Tokens are in the
@@ -8,97 +9,72 @@
// Which is a format that can be fed directly into [Jison](http://github.com/zaach/jison).
// Set up the Lexer for both Node.js and the browser, depending on where we are.
if ((typeof process !== "undefined" && process !== null)) {
Rewriter = require('./rewriter').Rewriter;
_a = require('./rewriter');
Rewriter = _a.Rewriter;
_b = require('./helpers');
helpers = _b.helpers;
} else {
this.exports = this;
Rewriter = this.Rewriter;
helpers = this.helpers;
}
// Constants
// ---------
// Keywords that CoffeeScript shares in common with JavaScript.
JS_KEYWORDS = ["if", "else", "true", "false", "new", "return", "try", "catch", "finally", "throw", "break", "continue", "for", "in", "while", "delete", "instanceof", "typeof", "switch", "super", "extends", "class"];
// CoffeeScript-only keywords, which we're more relaxed about allowing. They can't
// be used standalone, but you can reference them as an attached property.
COFFEE_KEYWORDS = ["then", "unless", "yes", "no", "on", "off", "and", "or", "is", "isnt", "not", "of", "by", "where", "when"];
// The combined list of keywords is the superset that gets passed verbatim to
// the parser.
KEYWORDS = JS_KEYWORDS.concat(COFFEE_KEYWORDS);
// The list of keywords that are reserved by JavaScript, but not used, or are
// used by CoffeeScript internally. We throw an error when these are encountered,
// to avoid having a JavaScript error at runtime.
RESERVED = ["case", "default", "do", "function", "var", "void", "with", "const", "let", "debugger", "enum", "export", "import", "native", "__extends", "__hasProp"];
// The superset of both JavaScript keywords and reserved words, none of which may
// be used as identifiers or properties.
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED);
// Token matching regexes.
IDENTIFIER = /^([a-zA-Z$_](\w|\$)*)/;
NUMBER = /^(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i;
STRING = /^(""|''|"([\s\S]*?)([^\\]|\\\\)"|'([\s\S]*?)([^\\]|\\\\)')/;
HEREDOC = /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/;
JS = /^(``|`([\s\S]*?)([^\\]|\\\\)`)/;
OPERATOR = /^([+\*&|\/\-%=<>:!?]+)/;
WHITESPACE = /^([ \t]+)/;
COMMENT = /^(((\n?[ \t]*)?#[^\n]*)+)/;
CODE = /^((-|=)>)/;
REGEX = /^(\/(\S.*?)?([^\\]|\\\\)\/[imgy]{0,4})/;
MULTI_DENT = /^((\n([ \t]*))+)(\.)?/;
LAST_DENTS = /\n([ \t]*)/g;
LAST_DENT = /\n([ \t]*)/;
ASSIGNMENT = /^(:|=)$/;
// Token cleaning regexes.
JS_CLEANER = /(^`|`$)/g;
MULTILINER = /\n/g;
STRING_NEWLINES = /\n[ \t]*/g;
COMMENT_CLEANER = /(^[ \t]*#|\n[ \t]*$)/mg;
NO_NEWLINE = /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/;
HEREDOC_INDENT = /^[ \t]+/mg;
// Tokens which a regular expression will never immediately follow, but which
// a division operator might.
// See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
// Our list is shorter, due to sans-parentheses method calls.
NOT_REGEX = ['NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE'];
// Tokens which could legitimately be invoked or indexed. A opening
// parentheses or bracket following these tokens will be recorded as the start
// of a function invocation or indexing operation.
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@'];
// Tokens that indicate an access -- keywords immediately following will be
// treated as identifiers.
ACCESSORS = ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@'];
// Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
// occurs at the start of a line. We disambiguate these from trailing whens to
// avoid an ambiguity in the grammar.
BEFORE_WHEN = ['INDENT', 'OUTDENT', 'TERMINATOR'];
// Import the helpers we need.
_c = helpers;
include = _c.include;
count = _c.count;
starts = _c.starts;
compact = _c.compact;
balanced_string = _c.balanced_string;
// The Lexer Class
// ---------------
// The Lexer class reads a stream of CoffeeScript and divvys it up into tagged
// tokens. A minor bit of the ambiguity in the grammar has been avoided by
// tokens. Some potential ambiguity in the grammar has been avoided by
// pushing some extra smarts into the Lexer.
exports.Lexer = (function() {
Lexer = function Lexer() { };
// Scan by attempting to match tokens one at a time. Slow and steady.
Lexer.prototype.tokenize = function tokenize(code) {
Lexer = function() { };
// **tokenize** is the Lexer's main method. Scan by attempting to match tokens
// one at a time, using a regular expression anchored at the start of the
// remaining code, or a custom recursive token-matching method
// (for interpolations). When the next token has been recorded, we move forward
// within the code past the token, and begin again.
// Each tokenizing method is responsible for incrementing `@i` by the number of
// characters it has consumed. `@i` can be thought of as our finger on the page
// of source.
// Before returning the token stream, run it through the [Rewriter](rewriter.html)
// unless explicitly asked not to.
Lexer.prototype.tokenize = function(code, options) {
var o;
code = code.replace(/(\r|\s+$)/g, '');
o = options || {};
this.code = code;
// The remainder of the source code.
this.i = 0;
// Current character position we're parsing.
this.line = 0;
this.line = o.line || 0;
// The current line.
this.indent = 0;
// The current indent level.
// The current indentation level.
this.indents = [];
// The stack of all indent levels we are currently within.
// The stack of all current indentation levels.
this.tokens = [];
// Collection of all parsed tokens in the form ['TOKEN_TYPE', value, line]
// Stream of parsed tokens in the form ['TYPE', value, line]
while (this.i < this.code.length) {
this.chunk = this.code.slice(this.i);
this.extract_next_token();
}
this.close_indentation();
if (o.rewrite === false) {
return this.tokens;
}
return (new Rewriter()).rewrite(this.tokens);
};
// At every position, run through this list of attempted matches,
// short-circuiting if any of them succeed.
Lexer.prototype.extract_next_token = function extract_next_token() {
// short-circuiting if any of them succeed. Their order determines precedence:
// `@literal_token` is the fallback catch-all.
Lexer.prototype.extract_next_token = function() {
if (this.extension_token()) {
return null;
}
if (this.identifier_token()) {
return null;
}
@@ -108,12 +84,6 @@
if (this.heredoc_token()) {
return null;
}
if (this.string_token()) {
return null;
}
if (this.js_token()) {
return null;
}
if (this.regex_token()) {
return null;
}
@@ -126,121 +96,214 @@
if (this.whitespace_token()) {
return null;
}
if (this.js_token()) {
return null;
}
if (this.string_token()) {
return null;
}
return this.literal_token();
};
// Tokenizers
// ----------
// Language extensions get the highest priority, first chance to tag tokens
// as something else.
Lexer.prototype.extension_token = function() {
var _d, _e, _f, extension;
_e = Lexer.extensions;
for (_d = 0, _f = _e.length; _d < _f; _d++) {
extension = _e[_d];
if (extension.call(this)) {
return true;
}
}
return false;
};
// Matches identifying literals: variables, keywords, method names, etc.
Lexer.prototype.identifier_token = function identifier_token() {
var id, tag;
if (!((id = this.match(IDENTIFIER, 1)))) {
// Check to ensure that JavaScript reserved words aren't being used as
// identifiers. Because CoffeeScript reserves a handful of keywords that are
// allowed in JavaScript, we're careful not to tag them as keywords when
// referenced as property names here, so you can still do `jQuery.is()` even
// though `is` means `===` otherwise.
Lexer.prototype.identifier_token = function() {
var accessed, id, tag;
if (!(id = this.match(IDENTIFIER, 1))) {
return false;
}
this.name_access_type();
accessed = include(ACCESSORS, this.tag(0));
tag = 'IDENTIFIER';
if (include(KEYWORDS, id) && !(include(ACCESSORS, this.tag(0)) && !this.prev().spaced)) {
if (!accessed && include(KEYWORDS, id)) {
tag = id.toUpperCase();
}
if (include(RESERVED, id)) {
this.identifier_error(id);
}
if (tag === 'WHEN' && include(BEFORE_WHEN, this.tag())) {
if (tag === 'WHEN' && include(LINE_BREAK, this.tag())) {
tag = 'LEADING_WHEN';
}
this.token(tag, id);
this.i += id.length;
if (!(accessed)) {
if (include(COFFEE_ALIASES, id)) {
tag = (id = CONVERSIONS[id]);
}
if (this.prev() && this.prev()[0] === 'ASSIGN' && include(HALF_ASSIGNMENTS, tag)) {
return this.tag_half_assignment(tag);
}
}
this.token(tag, id);
return true;
};
// Matches numbers, including decimals, hex, and exponential notation.
Lexer.prototype.number_token = function number_token() {
Lexer.prototype.number_token = function() {
var number;
if (!((number = this.match(NUMBER, 1)))) {
if (!(number = this.match(NUMBER, 1))) {
return false;
}
this.token('NUMBER', number);
this.i += number.length;
return true;
};
// Matches strings, including multi-line strings.
Lexer.prototype.string_token = function string_token() {
var escaped, string;
if (!((string = this.match(STRING, 1)))) {
// Matches strings, including multi-line strings. Ensures that quotation marks
// are balanced within the string's contents, and within nested interpolations.
Lexer.prototype.string_token = function() {
var string;
if (!(starts(this.chunk, '"') || starts(this.chunk, "'"))) {
return false;
}
escaped = string.replace(STRING_NEWLINES, " \\\n");
this.token('STRING', escaped);
if (!(string = this.balanced_token(['"', '"'], ['${', '}']) || this.balanced_token(["'", "'"]))) {
return false;
}
this.interpolate_string(string.replace(STRING_NEWLINES, " \\\n"));
this.line += count(string, "\n");
this.i += string.length;
return true;
};
// Matches heredocs, adjusting indentation to the correct level.
Lexer.prototype.heredoc_token = function heredoc_token() {
var doc, match;
if (!((match = this.chunk.match(HEREDOC)))) {
// Matches heredocs, adjusting indentation to the correct level, as heredocs
// preserve whitespace, but ignore indentation to the left.
Lexer.prototype.heredoc_token = function() {
var doc, match, quote;
if (!(match = this.chunk.match(HEREDOC))) {
return false;
}
doc = this.sanitize_heredoc(match[2] || match[4]);
this.token('STRING', '"' + doc + '"');
quote = match[1].substr(0, 1);
doc = this.sanitize_heredoc(match[2] || match[4], {
quote: quote
});
this.interpolate_string(("" + quote + doc + quote));
this.line += count(match[1], "\n");
this.i += match[1].length;
return true;
};
// Matches interpolated JavaScript.
Lexer.prototype.js_token = function js_token() {
// Matches and conumes comments. We pass through comments into JavaScript,
// so they're treated as real tokens, like any other part of the language.
Lexer.prototype.comment_token = function() {
var comment, i, lines, match;
if (!(match = this.chunk.match(COMMENT))) {
return false;
}
if (match[3]) {
comment = this.sanitize_heredoc(match[3], {
herecomment: true
});
this.token('HERECOMMENT', comment.split(MULTILINER));
} else {
lines = compact(match[1].replace(COMMENT_CLEANER, '').split(MULTILINER));
i = this.tokens.length - 1;
if (this.unfinished()) {
while (this.tokens[i] && !include(LINE_BREAK, this.tokens[i][0])) {
i -= 1;
}
}
this.tokens.splice(i + 1, 0, ['COMMENT', lines, this.line], ['TERMINATOR', '\n', this.line]);
}
this.line += count(match[1], "\n");
this.i += match[1].length;
return true;
};
// Matches JavaScript interpolated directly into the source via backticks.
Lexer.prototype.js_token = function() {
var script;
if (!((script = this.match(JS, 1)))) {
if (!(starts(this.chunk, '`'))) {
return false;
}
if (!(script = this.balanced_token(['`', '`']))) {
return false;
}
this.token('JS', script.replace(JS_CLEANER, ''));
this.i += script.length;
return true;
};
// Matches regular expression literals.
Lexer.prototype.regex_token = function regex_token() {
var regex;
if (!((regex = this.match(REGEX, 1)))) {
// Matches regular expression literals. Lexing regular expressions is difficult
// to distinguish from division, so we borrow some basic heuristics from
// JavaScript and Ruby, borrow slash balancing from `@balanced_token`, and
// borrow interpolation from `@interpolate_string`.
Lexer.prototype.regex_token = function() {
var end, flags, regex, str;
if (!(this.chunk.match(REGEX_START))) {
return false;
}
if (include(NOT_REGEX, this.tag())) {
return false;
}
this.token('REGEX', regex);
if (!(regex = this.balanced_token(['/', '/']))) {
return false;
}
if (!(end = this.chunk.substr(regex.length).match(REGEX_END))) {
return false;
}
if (end[2]) {
regex += (flags = end[2]);
}
if (regex.match(REGEX_INTERPOLATION)) {
str = regex.substring(1).split('/')[0];
str = str.replace(REGEX_ESCAPE, function(escaped) {
return '\\' + escaped;
});
this.tokens = this.tokens.concat([['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]);
this.interpolate_string(("\"" + str + "\""), true);
this.tokens = this.tokens.concat([[',', ','], ['STRING', ("\"" + flags + "\"")], [')', ')'], [')', ')']]);
} else {
this.token('REGEX', regex);
}
this.i += regex.length;
return true;
};
// Matches and conumes comments.
Lexer.prototype.comment_token = function comment_token() {
var comment, lines;
if (!((comment = this.match(COMMENT, 1)))) {
return false;
}
this.line += (comment.match(MULTILINER) || []).length;
lines = comment.replace(COMMENT_CLEANER, '').split(MULTILINER);
this.token('COMMENT', compact(lines));
this.token('TERMINATOR', "\n");
this.i += comment.length;
return true;
// Matches a token in which which the passed delimiter pairs must be correctly
// balanced (ie. strings, JS literals).
Lexer.prototype.balanced_token = function() {
var delimited;
var _d = arguments.length, _e = _d >= 1;
delimited = __slice.call(arguments, 0, _d - 0);
return balanced_string(this.chunk, delimited);
};
// Matches newlines, indents, and outdents, and determines which is which.
Lexer.prototype.line_token = function line_token() {
// If we can detect that the current line is continued onto the the next line,
// then the newline is suppressed:
// elements
// .each( ... )
// .map( ... )
// Keeps track of the level of indentation, because a single outdent token
// can close multiple indents, so we need to know how far in we happen to be.
Lexer.prototype.line_token = function() {
var diff, indent, next_character, no_newlines, prev, size;
if (!((indent = this.match(MULTI_DENT, 1)))) {
if (!(indent = this.match(MULTI_DENT, 1))) {
return false;
}
this.line += indent.match(MULTILINER).length;
this.line += count(indent, "\n");
this.i += indent.length;
prev = this.prev(2);
size = indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length;
next_character = this.chunk.match(MULTI_DENT)[4];
no_newlines = next_character === '.' || (this.value() && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE));
no_newlines = next_character === '.' || this.unfinished();
if (size === this.indent) {
if (no_newlines) {
return this.suppress_newlines(indent);
return this.suppress_newlines();
}
return this.newline_token(indent);
} else if (size > this.indent) {
if (no_newlines) {
return this.suppress_newlines(indent);
return this.suppress_newlines();
}
diff = size - this.indent;
this.token('INDENT', diff);
@@ -251,9 +314,9 @@
this.indent = size;
return true;
};
// Record an outdent token or tokens, if we happen to be moving back inwards
// past multiple recorded indents.
Lexer.prototype.outdent_token = function outdent_token(move_out, no_newlines) {
// Record an outdent token or multiple tokens, if we happen to be moving back
// inwards past several recorded indents.
Lexer.prototype.outdent_token = function(move_out, no_newlines) {
var last_indent;
while (move_out > 0 && this.indents.length) {
last_indent = this.indents.pop();
@@ -267,9 +330,9 @@
};
// Matches and consumes non-meaningful whitespace. Tag the previous token
// as being "spaced", because there are some cases where it makes a difference.
Lexer.prototype.whitespace_token = function whitespace_token() {
Lexer.prototype.whitespace_token = function() {
var prev, space;
if (!((space = this.match(WHITESPACE, 1)))) {
if (!(space = this.match(WHITESPACE, 1))) {
return false;
}
prev = this.prev();
@@ -279,8 +342,8 @@
this.i += space.length;
return true;
};
// Generate a newline token. Multiple newlines get merged together.
Lexer.prototype.newline_token = function newline_token(newlines) {
// Generate a newline token. Consecutive newlines get merged together.
Lexer.prototype.newline_token = function(newlines) {
if (!(this.tag() === 'TERMINATOR')) {
this.token('TERMINATOR', "\n");
}
@@ -288,7 +351,7 @@
};
// Use a `\` at a line-ending to suppress the newline.
// The slash is removed here once its job is done.
Lexer.prototype.suppress_newlines = function suppress_newlines(newlines) {
Lexer.prototype.suppress_newlines = function() {
if (this.value() === "\\") {
this.tokens.pop();
}
@@ -296,16 +359,19 @@
};
// We treat all other single characters as a token. Eg.: `( ) , . !`
// Multi-character operators are also literal tokens, so that Jison can assign
// the proper order of operations.
Lexer.prototype.literal_token = function literal_token() {
var match, not_spaced, tag, value;
// the proper order of operations. There are some symbols that we tag specially
// here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish
// parentheses that indicate a method call from regular parentheses, and so on.
Lexer.prototype.literal_token = function() {
var match, prev_spaced, space, tag, value;
match = this.chunk.match(OPERATOR);
value = match && match[1];
space = match && match[2];
if (value && value.match(CODE)) {
this.tag_parameters();
}
value = value || this.chunk.substr(0, 1);
not_spaced = !this.prev() || !this.prev().spaced;
prev_spaced = this.prev() && this.prev().spaced;
tag = value;
if (value.match(ASSIGNMENT)) {
tag = 'ASSIGN';
@@ -314,14 +380,14 @@
}
} else if (value === ';') {
tag = 'TERMINATOR';
} else if (value === '[' && this.tag() === '?' && not_spaced) {
} else if (value === '[' && this.tag() === '?' && !prev_spaced) {
tag = 'SOAKED_INDEX_START';
this.soaked_index = true;
this.tokens.pop();
} else if (value === ']' && this.soaked_index) {
tag = 'SOAKED_INDEX_END';
this.soaked_index = false;
} else if (include(CALLABLE, this.tag()) && not_spaced) {
} else if (include(CALLABLE, this.tag()) && !prev_spaced) {
if (value === '(') {
tag = 'CALL_START';
}
@@ -329,15 +395,18 @@
tag = 'INDEX_START';
}
}
this.token(tag, value);
this.i += value.length;
if (space && prev_spaced && this.prev()[0] === 'ASSIGN' && include(HALF_ASSIGNMENTS, tag)) {
return this.tag_half_assignment(tag);
}
this.token(tag, value);
return true;
};
// Token Manipulators
// ------------------
// As we consume a new `IDENTIFIER`, look at the previous token to determine
// if it's a special kind of accessor.
Lexer.prototype.name_access_type = function name_access_type() {
Lexer.prototype.name_access_type = function() {
if (this.value() === '::') {
this.tag(1, 'PROTOTYPE_ACCESS');
}
@@ -350,19 +419,34 @@
}
}
};
// Sanitize a heredoc by escaping double quotes and erasing all external
// indentation on the left-hand side.
Lexer.prototype.sanitize_heredoc = function sanitize_heredoc(doc) {
var indent;
indent = (doc.match(HEREDOC_INDENT) || ['']).sort()[0];
return doc.replace(new RegExp("^" + indent, 'gm'), '').replace(MULTILINER, "\\n").replace(/"/g, '\\"');
// Sanitize a heredoc or herecomment by escaping internal double quotes and
// erasing all external indentation on the left-hand side.
Lexer.prototype.sanitize_heredoc = function(doc, options) {
var _d, attempt, indent, match;
while (match = HEREDOC_INDENT.exec(doc)) {
attempt = (typeof (_d = match[2]) !== "undefined" && _d !== null) ? match[2] : match[3];
if (!indent || attempt.length < indent.length) {
indent = attempt;
}
}
doc = doc.replace(new RegExp("^" + indent, 'gm'), '');
if (options.herecomment) {
return doc;
}
return doc.replace(MULTILINER, "\\n").replace(new RegExp(options.quote, 'g'), '\\"');
};
// A source of ambiguity in our grammar was parameter lists in function
// definitions (as opposed to argument lists in function calls). Tag
// parameter identifiers in order to avoid this. Also, parameter lists can
// make use of splats.
Lexer.prototype.tag_parameters = function tag_parameters() {
var _a, i, tok;
// Tag a half assignment.
Lexer.prototype.tag_half_assignment = function(tag) {
var last;
last = this.tokens.pop();
this.tokens.push([("" + tag + "="), ("" + tag + "="), last[2]]);
return true;
};
// A source of ambiguity in our grammar used to be parameter lists in function
// definitions versus argument lists in function calls. Walk backwards, tagging
// parameters specially in order to make things easier for the parser.
Lexer.prototype.tag_parameters = function() {
var _d, i, tok;
if (this.tag() !== ')') {
return null;
}
@@ -373,100 +457,250 @@
if (!tok) {
return null;
}
if ((_a = tok[0]) === 'IDENTIFIER') {
if ((_d = tok[0]) === 'IDENTIFIER') {
tok[0] = 'PARAM';
} else if (_a === ')') {
} else if (_d === ')') {
tok[0] = 'PARAM_END';
} else if (_a === '(') {
return (tok[0] = 'PARAM_START');
} else if (_d === '(' || _d === 'CALL_START') {
tok[0] = 'PARAM_START';
return tok[0];
}
}
return true;
};
// Close up all remaining open blocks at the end of the file.
Lexer.prototype.close_indentation = function close_indentation() {
Lexer.prototype.close_indentation = function() {
return this.outdent_token(this.indent);
};
// Error for when you try to use a forbidden word in JavaScript as
// The error for when you try to use a forbidden word in JavaScript as
// an identifier.
Lexer.prototype.identifier_error = function identifier_error(word) {
throw new Error('SyntaxError: Reserved word "' + word + '" on line ' + this.line);
Lexer.prototype.identifier_error = function(word) {
throw new Error(("SyntaxError: Reserved word \"" + word + "\" on line " + (this.line + 1)));
};
// Error for when you try to assign to a reserved word in JavaScript,
// The error for when you try to assign to a reserved word in JavaScript,
// like "function" or "default".
Lexer.prototype.assignment_error = function assignment_error() {
throw new Error('SyntaxError: Reserved word "' + this.value() + '" on line ' + this.line + ' can\'t be assigned');
Lexer.prototype.assignment_error = function() {
throw new Error(("SyntaxError: Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned"));
};
// Expand variables and expressions inside double-quoted strings using
// [ECMA Harmony's interpolation syntax](http://wiki.ecmascript.org/doku.php?id=strawman:string_interpolation)
// for substitution of bare variables as well as arbitrary expressions.
// "Hello $name."
// "Hello ${name.capitalize()}."
// If it encounters an interpolation, this method will recursively create a
// new Lexer, tokenize the interpolated contents, and merge them into the
// token stream.
Lexer.prototype.interpolate_string = function(str, escape_quotes) {
var _d, _e, _f, _g, _h, _i, _j, escaped, expr, group, i, idx, inner, interp, interpolated, lexer, match, nested, pi, quote, tag, tok, token, tokens, value;
if (str.length < 3 || !starts(str, '"')) {
return this.token('STRING', str);
} else {
lexer = new Lexer();
tokens = [];
quote = str.substring(0, 1);
_d = [1, 1];
i = _d[0];
pi = _d[1];
while (i < str.length - 1) {
if (starts(str, '\\', i)) {
i += 1;
} else if ((match = str.substring(i).match(INTERPOLATION))) {
_e = match;
group = _e[0];
interp = _e[1];
if (starts(interp, '@')) {
interp = ("this." + (interp.substring(1)));
}
if (pi < i) {
tokens.push(['STRING', ("" + quote + (str.substring(pi, i)) + quote)]);
}
tokens.push(['IDENTIFIER', interp]);
i += group.length - 1;
pi = i + 1;
} else if ((expr = balanced_string(str.substring(i), [['${', '}']]))) {
if (pi < i) {
tokens.push(['STRING', ("" + quote + (str.substring(pi, i)) + quote)]);
}
inner = expr.substring(2, expr.length - 1);
if (inner.length) {
nested = lexer.tokenize(("(" + inner + ")"), {
line: this.line
});
_f = nested;
for (idx = 0, _g = _f.length; idx < _g; idx++) {
tok = _f[idx];
tok[0] === 'CALL_END' ? (tok[0] = ')') : null;
}
nested.pop();
tokens.push(['TOKENS', nested]);
} else {
tokens.push(['STRING', ("" + quote + quote)]);
}
i += expr.length - 1;
pi = i + 1;
}
i += 1;
}
if (pi < i && pi < str.length - 1) {
tokens.push(['STRING', ("" + quote + (str.substring(pi, i)) + quote)]);
}
if (!(tokens[0][0] === 'STRING')) {
tokens.unshift(['STRING', '""']);
}
interpolated = tokens.length > 1;
if (interpolated) {
this.token('(', '(');
}
_h = tokens;
for (i = 0, _i = _h.length; i < _i; i++) {
token = _h[i];
_j = token;
tag = _j[0];
value = _j[1];
if (tag === 'TOKENS') {
this.tokens = this.tokens.concat(value);
} else if (tag === 'STRING' && escape_quotes) {
escaped = value.substring(1, value.length - 1).replace(/"/g, '\\"');
this.token(tag, ("\"" + escaped + "\""));
} else {
this.token(tag, value);
}
if (i < tokens.length - 1) {
this.token('+', '+');
}
}
if (interpolated) {
this.token(')', ')');
}
return tokens;
}
};
// Helpers
// -------
// Add a token to the results, taking note of the line number.
Lexer.prototype.token = function token(tag, value) {
Lexer.prototype.token = function(tag, value) {
return this.tokens.push([tag, value, this.line]);
};
// Peek at a tag in the current token stream.
Lexer.prototype.tag = function tag(index, tag) {
Lexer.prototype.tag = function(index, new_tag) {
var tok;
if (!((tok = this.prev(index)))) {
if (!(tok = this.prev(index))) {
return null;
}
if ((typeof tag !== "undefined" && tag !== null)) {
return (tok[0] = tag);
if ((typeof new_tag !== "undefined" && new_tag !== null)) {
tok[0] = new_tag;
return tok[0];
}
return tok[0];
};
// Peek at a value in the current token stream.
Lexer.prototype.value = function value(index, val) {
Lexer.prototype.value = function(index, val) {
var tok;
if (!((tok = this.prev(index)))) {
if (!(tok = this.prev(index))) {
return null;
}
if ((typeof val !== "undefined" && val !== null)) {
return (tok[1] = val);
tok[1] = val;
return tok[1];
}
return tok[1];
};
// Peek at a previous token, entire.
Lexer.prototype.prev = function prev(index) {
Lexer.prototype.prev = function(index) {
return this.tokens[this.tokens.length - (index || 1)];
};
// Attempt to match a string against the current chunk, returning the indexed
// match if successful, and `false` otherwise.
Lexer.prototype.match = function match(regex, index) {
Lexer.prototype.match = function(regex, index) {
var m;
if (!((m = this.chunk.match(regex)))) {
if (!(m = this.chunk.match(regex))) {
return false;
}
if (m) {
return m[index];
} else {
return false;
}
return m ? m[index] : false;
};
// Are we in the midst of an unfinished expression?
Lexer.prototype.unfinished = function() {
var prev;
prev = this.prev(2);
return this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE);
};
// Lexer Properties
// ----------------
// There are no exensions to the core lexer by default.
Lexer.extensions = [];
return Lexer;
}).call(this);
// Utility Functions
// -----------------
// Does a list include a value?
include = function include(list, value) {
return list.indexOf(value) >= 0;
};
// Trim out all falsy values from an array.
compact = function compact(array) {
var _a, _b, _c, _d, item;
_a = []; _b = array;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
item = _b[_c];
if (item) {
_a.push(item);
}
}
return _a;
};
// Count the number of occurences of a character in a string.
count = function count(string, letter) {
var num, pos;
num = 0;
pos = string.indexOf(letter);
while (pos !== -1) {
num += 1;
pos = string.indexOf(letter, pos + 1);
}
return num;
// Constants
// ---------
// Keywords that CoffeeScript shares in common with JavaScript.
JS_KEYWORDS = ["if", "else", "true", "false", "new", "return", "try", "catch", "finally", "throw", "break", "continue", "for", "in", "while", "delete", "instanceof", "typeof", "switch", "super", "extends", "class", "this", "null"];
// CoffeeScript-only keywords, which we're more relaxed about allowing. They can't
// be used standalone, but you can reference them as an attached property.
COFFEE_ALIASES = ["and", "or", "is", "isnt", "not"];
COFFEE_KEYWORDS = COFFEE_ALIASES.concat(["then", "unless", "until", "yes", "no", "on", "off", "of", "by", "where", "when"]);
// The combined list of keywords is the superset that gets passed verbatim to
// the parser.
KEYWORDS = JS_KEYWORDS.concat(COFFEE_KEYWORDS);
// The list of keywords that are reserved by JavaScript, but not used, or are
// used by CoffeeScript internally. We throw an error when these are encountered,
// to avoid having a JavaScript error at runtime.
RESERVED = ["case", "default", "do", "function", "var", "void", "with", "const", "let", "enum", "export", "import", "native"];
// The superset of both JavaScript keywords and reserved words, none of which may
// be used as identifiers or properties.
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED);
// Token matching regexes.
IDENTIFIER = /^([a-zA-Z\$_](\w|\$)*)/;
NUMBER = /^(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i;
HEREDOC = /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/;
INTERPOLATION = /^\$([a-zA-Z_@]\w*(\.\w+)*)/;
OPERATOR = /^([+\*&|\/\-%=<>:!?]+)([ \t]*)/;
WHITESPACE = /^([ \t]+)/;
COMMENT = /^((\n?[ \t]*)?#{3}(?!#)\n*([\s\S]*?)\n*([ \t]*)#{3}|((\n?[ \t]*)?#[^\n]*)+)/;
CODE = /^((-|=)>)/;
MULTI_DENT = /^((\n([ \t]*))+)(\.)?/;
LAST_DENTS = /\n([ \t]*)/g;
LAST_DENT = /\n([ \t]*)/;
ASSIGNMENT = /^(:|=)$/;
// Regex-matching-regexes.
REGEX_START = /^\/[^\/ ]/;
REGEX_INTERPOLATION = /([^\\]\$[a-zA-Z_@]|[^\\]\$\{.*[^\\]\})/;
REGEX_END = /^(([imgy]{1,4})\b|\W)/;
REGEX_ESCAPE = /\\[^\$]/g;
// Token cleaning regexes.
JS_CLEANER = /(^`|`$)/g;
MULTILINER = /\n/g;
STRING_NEWLINES = /\n[ \t]*/g;
COMMENT_CLEANER = /(^[ \t]*#|\n[ \t]*$)/mg;
NO_NEWLINE = /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/;
HEREDOC_INDENT = /(\n+([ \t]*)|^([ \t]+))/g;
// Tokens which a regular expression will never immediately follow, but which
// a division operator might.
// See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
// Our list is shorter, due to sans-parentheses method calls.
NOT_REGEX = ['NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']'];
// Tokens which could legitimately be invoked or indexed. A opening
// parentheses or bracket following these tokens will be recorded as the start
// of a function invocation or indexing operation.
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS'];
// Tokens that indicate an access -- keywords immediately following will be
// treated as identifiers.
ACCESSORS = ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@'];
// Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
// 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'];
// Half-assignments...
HALF_ASSIGNMENTS = ['-', '+', '/', '*', '%', '||', '&&', '?'];
// Conversions from CoffeeScript operators into JavaScript ones.
CONVERSIONS = {
'and': '&&',
'or': '||',
'is': '==',
'isnt': '!=',
'not': '!'
};
})();

View File

@@ -1,44 +0,0 @@
(function(){
var coffee, factories, file, loader, os, puts;
// The Narwhal-compatibility wrapper for CoffeeScript.
// Require external dependencies.
os = require('os');
file = require('file');
coffee = require('./coffee-script');
// Alias print to "puts", for Node.js compatibility:
puts = print;
// Compile a string of CoffeeScript into JavaScript.
exports.compile = function compile(source) {
return coffee.compile(source);
};
// Compile a given CoffeeScript file into JavaScript.
exports.compileFile = function compileFile(path) {
return coffee.compile(file.read(path));
};
// Make a factory for the CoffeeScript environment.
exports.makeNarwhalFactory = function makeNarwhalFactory(path) {
var code, factoryText;
code = exports.compileFile(path);
factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
if (system.engine === "rhino") {
return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
} else {
// eval requires parentheses, but parentheses break compileFunction.
return eval("(" + factoryText + ")");
}
};
// The Narwhal loader for '.coffee' files.
factories = {};
loader = {};
// Reload the coffee-script environment from source.
loader.reload = function reload(topId, path) {
return factories[topId] = function() {
return exports.makeNarwhalFactory(path);
};
};
// Ensure that the coffee-script environment is loaded.
loader.load = function load(topId, path) {
return factories[topId] = factories[topId] || this.reload(topId, path);
};
require.loader.loaders.unshift([".coffee", loader]);
})();

File diff suppressed because it is too large Load Diff

View File

@@ -1,38 +1,43 @@
(function(){
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, build_rule, build_rules, normalize_arguments;
// Create an OptionParser with a list of valid options, in the form:
// [short-flag (optional), long-flag, description]
// And an optional banner for the usage help.
// A simple **OptionParser** class to parse option flags from the command-line.
// Use it like so:
// parser: new OptionParser switches, help_banner
// options: parser.parse process.argv
exports.OptionParser = (function() {
OptionParser = function OptionParser(rules, banner) {
OptionParser = function(rules, banner) {
this.banner = banner;
this.rules = build_rules(rules);
return this;
};
// Parse the argument array, populating an options object with all of the
// specified options, and returning it. options.arguments will be an array
// containing the remaning non-option arguments.
OptionParser.prototype.parse = function parse(args) {
// Initialize with a list of valid options, in the form:
// [short-flag, long-flag, description]
// Along with an an optional banner for the usage help.
// Parse the list of arguments, populating an `options` object with all of the
// specified options, and returning it. `options.arguments` will be an array
// containing the remaning non-option arguments. This is a simpler API than
// many option parsers that allow you to attach callback actions for every
// flag. Instead, you're responsible for interpreting the options object.
OptionParser.prototype.parse = function(args) {
var _a, _b, _c, arg, is_option, matched_rule, options, rule;
arguments = Array.prototype.slice.call(arguments, 0);
options = {
arguments: []
};
args = normalize_arguments(args);
while (arg = args.shift()) {
while ((arg = args.shift())) {
is_option = !!(arg.match(LONG_FLAG) || arg.match(SHORT_FLAG));
matched_rule = false;
_a = this.rules;
for (_b = 0, _c = _a.length; _b < _c; _b++) {
rule = _a[_b];
if (rule.letter === arg || rule.flag === arg) {
_b = this.rules;
for (_a = 0, _c = _b.length; _a < _c; _a++) {
rule = _b[_a];
if (rule.short_flag === arg || rule.long_flag === arg) {
options[rule.name] = rule.has_argument ? args.shift() : true;
matched_rule = true;
break;
}
}
if (is_option && !matched_rule) {
throw new Error("unrecognized option: " + arg);
throw new Error(("unrecognized option: " + arg));
}
if (!(is_option)) {
options.arguments.push(arg);
@@ -40,77 +45,82 @@
}
return options;
};
// Return the help text for this OptionParser, for --help and such.
OptionParser.prototype.help = function help() {
var _a, _b, _c, _d, _e, _f, _g, _h, i, let_part, lines, rule, spaces;
// Return the help text for this **OptionParser**, listing and describing all
// of the valid options, for `--help` and such.
OptionParser.prototype.help = function() {
var _a, _b, _c, _d, _e, _f, i, let_part, lines, rule, spaces;
lines = ['Available options:'];
if (this.banner) {
lines.unshift(this.banner + '\n');
lines.unshift(("" + this.banner + "\n"));
}
_a = this.rules;
for (_b = 0, _c = _a.length; _b < _c; _b++) {
rule = _a[_b];
spaces = 15 - rule.flag.length;
_b = this.rules;
for (_a = 0, _c = _b.length; _a < _c; _a++) {
rule = _b[_a];
spaces = 15 - rule.long_flag.length;
spaces = spaces > 0 ? (function() {
_d = []; _g = 0; _h = spaces;
for (_f = 0, i = _g; (_g <= _h ? i <= _h : i >= _h); (_g <= _h ? i += 1 : i -= 1), _f++) {
_d = []; _e = 0; _f = spaces;
for (i = _e; (_e <= _f ? i <= _f : i >= _f); (_e <= _f ? i += 1 : i -= 1)) {
_d.push(' ');
}
return _d;
}).call(this).join('') : '';
let_part = rule.letter ? rule.letter + ', ' : ' ';
lines.push(' ' + let_part + rule.flag + spaces + rule.description);
})().join('') : '';
let_part = rule.short_flag ? rule.short_flag + ', ' : ' ';
lines.push((" " + let_part + rule.long_flag + spaces + rule.description));
}
return lines.join('\n');
return "\n" + (lines.join('\n')) + "\n";
};
return OptionParser;
}).call(this);
})();
// Helpers
// -------
// Regex matchers for option flags.
LONG_FLAG = /^(--\w[\w\-]+)/;
SHORT_FLAG = /^(-\w)/;
MULTI_FLAG = /^-(\w{2,})/;
OPTIONAL = /\[(.+)\]/;
// Build rules from a list of valid switch tuples in the form:
// [letter-flag, long-flag, help], or [long-flag, help].
build_rules = function build_rules(rules) {
// Build and return the list of option rules. If the optional *short-flag* is
// unspecified, leave it out by padding with `null`.
build_rules = function(rules) {
var _a, _b, _c, _d, tuple;
_a = []; _b = rules;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
tuple = _b[_c];
_a = []; _c = rules;
for (_b = 0, _d = _c.length; _b < _d; _b++) {
tuple = _c[_b];
_a.push((function() {
if (tuple.length < 3) {
tuple.unshift(null);
}
return build_rule.apply(this, tuple);
}).call(this));
})());
}
return _a;
};
// Build a rule from a short-letter-flag, long-form-flag, and help text.
build_rule = function build_rule(letter, flag, description) {
// Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the
// description of what the option does.
build_rule = function(short_flag, long_flag, description) {
var match;
match = flag.match(OPTIONAL);
flag = flag.match(LONG_FLAG)[1];
match = long_flag.match(OPTIONAL);
long_flag = long_flag.match(LONG_FLAG)[1];
return {
name: flag.substr(2),
letter: letter,
flag: flag,
name: long_flag.substr(2),
short_flag: short_flag,
long_flag: long_flag,
description: description,
has_argument: !!(match && match[1])
};
};
// Normalize arguments by expanding merged flags into multiple flags.
normalize_arguments = function normalize_arguments(args) {
// Normalize arguments by expanding merged flags into multiple flags. This allows
// you to have `-wl` be the same as `--watch --lint`.
normalize_arguments = function(args) {
var _a, _b, _c, _d, _e, _f, arg, l, match, result;
args = args.slice(0);
result = [];
_a = args;
for (_b = 0, _c = _a.length; _b < _c; _b++) {
arg = _a[_b];
_b = args;
for (_a = 0, _c = _b.length; _a < _c; _a++) {
arg = _b[_a];
if ((match = arg.match(MULTI_FLAG))) {
_d = match[1].split('');
for (_e = 0, _f = _d.length; _e < _f; _e++) {
l = _d[_e];
_e = match[1].split('');
for (_d = 0, _f = _e.length; _d < _f; _d++) {
l = _e[_d];
result.push('-' + l);
}
} else {

File diff suppressed because one or more lines are too long

View File

@@ -1,22 +1,31 @@
(function(){
var coffee, prompt, quit, readline;
// A CoffeeScript port/version of the Node.js REPL.
// Required modules.
coffee = require('coffee-script');
// Shortcut variables.
var CoffeeScript, helpers, prompt, run, stdin;
// A very simple Read-Eval-Print-Loop. Compiles one line at a time to JavaScript
// and evaluates it. Good for simple tests, or poking around the **Node.js** API.
// Using it looks like this:
// coffee> puts "$num bottles of beer" for num in [99..1]
// Require the **coffee-script** module to get access to the compiler.
CoffeeScript = require('./coffee-script');
helpers = require('./helpers').helpers;
// Our prompt.
prompt = 'coffee> ';
quit = function quit() {
return process.exit(0);
};
// The main REPL function. Called everytime a line of code is entered.
// Attempt to evaluate the command. If there's an exception, print it.
readline = function readline(code) {
// Quick alias for quitting the REPL.
helpers.extend(global, {
quit: function() {
return process.exit(0);
}
});
// The main REPL function. **run** is called every time a line of code is entered.
// Attempt to evaluate the command. If there's an exception, print it out instead
// of exiting.
run = function(buffer) {
var val;
try {
val = eval(coffee.compile(code, {
val = CoffeeScript.run(buffer.toString(), {
no_wrap: true,
globals: true
}));
globals: true,
source: 'repl'
});
if (val !== undefined) {
p(val);
}
@@ -25,8 +34,8 @@
}
return print(prompt);
};
// Start up the REPL.
process.stdio.addListener('data', readline);
process.stdio.open();
// Start up the REPL by opening **stdin** and listening for input.
stdin = process.openStdin();
stdin.addListener('data', run);
print(prompt);
})();

View File

@@ -1,62 +1,41 @@
(function(){
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_START, EXPRESSION_TAIL, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, pair;
var __hasProp = Object.prototype.hasOwnProperty;
if (!((typeof process !== "undefined" && process !== null))) {
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, helpers, include, pair;
var __slice = Array.prototype.slice, __bind = function(func, obj, args) {
return function() {
return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);
};
}, __hasProp = Object.prototype.hasOwnProperty;
// The CoffeeScript language has a good deal of optional syntax, implicit syntax,
// and shorthand syntax. This can greatly complicate a grammar and bloat
// the resulting parse table. Instead of making the parser handle it all, we take
// a series of passes over the token stream, using this **Rewriter** to convert
// shorthand into the unambiguous long form, add implicit indentation and
// parentheses, balance incorrect nestings, and generally clean things up.
// Set up exported variables for both Node.js and the browser.
if ((typeof process !== "undefined" && process !== null)) {
_a = require('./helpers');
helpers = _a.helpers;
} else {
this.exports = this;
helpers = this.helpers;
}
// Tokens that must be balanced.
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END'], ['SOAKED_INDEX_START', 'SOAKED_INDEX_END']];
// Tokens that signal the start of a balanced pair.
EXPRESSION_START = (function() {
_a = []; _b = BALANCED_PAIRS;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
pair = _b[_c];
_a.push(pair[0]);
}
return _a;
}).call(this);
// Tokens that signal the end of a balanced pair.
EXPRESSION_TAIL = (function() {
_e = []; _f = BALANCED_PAIRS;
for (_g = 0, _h = _f.length; _g < _h; _g++) {
pair = _f[_g];
_e.push(pair[1]);
}
return _e;
}).call(this);
// Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_TAIL);
// Tokens pairs that, in immediate succession, indicate an implicit call.
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END'];
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'INDENT', 'OUTDENT'];
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT', '@', '->', '=>', '[', '(', '{'];
// The inverse mappings of token pairs we're trying to fix up.
INVERSES = {};
_i = BALANCED_PAIRS;
for (_j = 0, _k = _i.length; _j < _k; _j++) {
pair = _i[_j];
INVERSES[pair[0]] = pair[1];
INVERSES[pair[1]] = pair[0];
}
// Single-line flavors of block expressions that have unclosed endings.
// The grammar can't disambiguate them, so we insert the implicit indentation.
SINGLE_LINERS = ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN'];
SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN'];
// In order to keep the grammar simple, the stream of tokens that the Lexer
// emits is rewritten by the Rewriter, smoothing out ambiguities, mis-nested
// indentation, and single-line flavors of expressions.
// Import the helpers we need.
_b = helpers;
include = _b.include;
// The **Rewriter** class is used by the [Lexer](lexer.html), directly against
// its internal array of tokens.
exports.Rewriter = (function() {
Rewriter = function Rewriter() { };
Rewriter = function() { };
// Rewrite the token stream in multiple passes, one logical filter at
// a time. This could certainly be changed into a single pass through the
// stream, with a big ol' efficient switch, but it's much nicer like this.
Rewriter.prototype.rewrite = function rewrite(tokens) {
// stream, with a big ol' efficient switch, but it's much nicer to work with
// like this. The order of these passes matters -- indentation must be
// corrected before implicit parentheses can be wrapped around blocks of code.
Rewriter.prototype.rewrite = function(tokens) {
this.tokens = tokens;
this.adjust_comments();
this.remove_leading_newlines();
this.remove_mid_expression_newlines();
this.move_commas_outside_outdents();
this.close_open_calls_and_indexes();
this.add_implicit_indentation();
this.add_implicit_parentheses();
@@ -67,8 +46,9 @@
// Rewrite the token stream, looking one token ahead and behind.
// Allow the return value of the block to tell us how many tokens to move
// forwards (or backwards) in the stream, to make sure we don't miss anything
// as the stream changes length under our feet.
Rewriter.prototype.scan_tokens = function scan_tokens(block) {
// as tokens are inserted and removed, and the stream changes length under
// our feet.
Rewriter.prototype.scan_tokens = function(block) {
var i, move;
i = 0;
while (true) {
@@ -81,18 +61,19 @@
return true;
};
// Massage newlines and indentations so that comments don't have to be
// correctly indented, or appear on their own line.
Rewriter.prototype.adjust_comments = function adjust_comments() {
return this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
var after;
// correctly indented, or appear on a line of their own.
Rewriter.prototype.adjust_comments = function() {
return this.scan_tokens(__bind(function(prev, token, post, i) {
var _c, after, before;
if (!(token[0] === 'COMMENT')) {
return 1;
}
after = this.tokens[i + 2];
_c = [this.tokens[i - 2], this.tokens[i + 2]];
before = _c[0];
after = _c[1];
if (after && after[0] === 'INDENT') {
this.tokens.splice(i + 2, 1);
this.tokens.splice(i, 0, after);
before && before[0] === 'OUTDENT' && post && (prev[0] === post[0]) && (post[0] === 'TERMINATOR') ? this.tokens.splice(i - 2, 1) : this.tokens.splice(i, 0, after);
return 1;
} else if (prev && prev[0] !== 'TERMINATOR' && prev[0] !== 'INDENT' && prev[0] !== 'OUTDENT') {
this.tokens.splice(i, 0, ['TERMINATOR', "\n", prev[2]]);
@@ -100,75 +81,54 @@
} else {
return 1;
}
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
}, this));
};
// Leading newlines would introduce an ambiguity in the grammar, so we
// dispatch them here.
Rewriter.prototype.remove_leading_newlines = function remove_leading_newlines() {
if (this.tokens[0][0] === 'TERMINATOR') {
return this.tokens.shift();
Rewriter.prototype.remove_leading_newlines = function() {
var _c;
_c = [];
while (this.tokens[0] && this.tokens[0][0] === 'TERMINATOR') {
_c.push(this.tokens.shift());
}
return _c;
};
// Some blocks occur in the middle of expressions -- when we're expecting
// this, remove their trailing newlines.
Rewriter.prototype.remove_mid_expression_newlines = function remove_mid_expression_newlines() {
return this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
if (!(post && EXPRESSION_CLOSE.indexOf(post[0]) >= 0 && token[0] === 'TERMINATOR')) {
Rewriter.prototype.remove_mid_expression_newlines = function() {
return this.scan_tokens(__bind(function(prev, token, post, i) {
if (!(post && include(EXPRESSION_CLOSE, post[0]) && token[0] === 'TERMINATOR')) {
return 1;
}
this.tokens.splice(i, 1);
return 0;
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
}, this));
};
// Make sure that we don't accidentally break trailing commas, which need
// to go on the outside of expression closers.
Rewriter.prototype.move_commas_outside_outdents = function move_commas_outside_outdents() {
return this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
if (token[0] === 'OUTDENT' && prev[0] === ',') {
this.tokens.splice(i, 1, token);
}
return 1;
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
};
// We've tagged the opening parenthesis of a method call, and the opening
// bracket of an indexing operation. Match them with their close.
Rewriter.prototype.close_open_calls_and_indexes = function close_open_calls_and_indexes() {
// The lexer has tagged the opening parenthesis of a method call, and the
// opening bracket of an indexing operation. Match them with their paired
// close.
Rewriter.prototype.close_open_calls_and_indexes = function() {
var brackets, parens;
parens = [0];
brackets = [0];
return this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
var _l;
if ((_l = token[0]) === 'CALL_START') {
return this.scan_tokens(__bind(function(prev, token, post, i) {
var _c;
if ((_c = token[0]) === 'CALL_START') {
parens.push(0);
} else if (_l === 'INDEX_START') {
} else if (_c === 'INDEX_START') {
brackets.push(0);
} else if (_l === '(') {
} else if (_c === '(') {
parens[parens.length - 1] += 1;
} else if (_l === '[') {
} else if (_c === '[') {
brackets[brackets.length - 1] += 1;
} else if (_l === ')') {
} else if (_c === ')') {
if (parens[parens.length - 1] === 0) {
parens.pop();
token[0] = 'CALL_END';
} else {
parens[parens.length - 1] -= 1;
}
} else if (_l === ']') {
} else if (_c === ']') {
if (brackets[brackets.length - 1] === 0) {
brackets.pop();
token[0] = 'INDEX_END';
@@ -177,79 +137,97 @@
}
}
return 1;
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
}, this));
};
// Methods may be optionally called without parentheses, for simple cases.
// Insert the implicit parentheses here, so that the parser doesn't have to
// deal with them.
Rewriter.prototype.add_implicit_parentheses = function add_implicit_parentheses() {
var stack;
Rewriter.prototype.add_implicit_parentheses = function() {
var close_calls, stack;
stack = [0];
return this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
var _l, _m, _n, _o, idx, last, size, stack_pointer, tag, tmp;
close_calls = __bind(function(i) {
var _c, _d, size, tmp;
_c = 0; _d = stack[stack.length - 1];
for (tmp = _c; (_c <= _d ? tmp < _d : tmp > _d); (_c <= _d ? tmp += 1 : tmp -= 1)) {
this.tokens.splice(i, 0, ['CALL_END', ')', this.tokens[i][2]]);
}
size = stack[stack.length - 1] + 1;
stack[stack.length - 1] = 0;
return size;
}, this);
return this.scan_tokens(__bind(function(prev, token, post, i) {
var j, nx, open, size, tag;
tag = token[0];
if (tag === 'INDENT') {
stack.push(0);
}
if (tag === 'OUTDENT') {
last = stack.pop();
stack[stack.length - 1] += last;
stack[stack.length - 2] += stack.pop();
}
if (IMPLICIT_END.indexOf(tag) >= 0 || !(typeof post !== "undefined" && post !== null)) {
if (tag === 'INDENT' && prev && IMPLICIT_BLOCK.indexOf(prev[0]) >= 0) {
return 1;
open = stack[stack.length - 1] > 0;
if (prev && prev.spaced && include(IMPLICIT_FUNC, prev[0]) && include(IMPLICIT_CALL, tag)) {
this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
stack[stack.length - 1] += 1;
if (include(EXPRESSION_START, tag)) {
stack.push(0);
}
if (stack[stack.length - 1] > 0 || tag === 'INDENT') {
idx = tag === 'OUTDENT' ? i + 1 : i;
stack_pointer = tag === 'INDENT' ? 2 : 1;
_n = 0; _o = stack[stack.length - stack_pointer];
for (_m = 0, tmp = _n; (_n <= _o ? tmp < _o : tmp > _o); (_n <= _o ? tmp += 1 : tmp -= 1), _m++) {
this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]);
return 2;
}
if (include(EXPRESSION_START, tag)) {
if (tag === 'INDENT' && !token.generated && open && !(prev && include(IMPLICIT_BLOCK, prev[0]))) {
size = close_calls(i);
stack.push(0);
return size;
}
stack.push(0);
return 1;
}
if (open && !token.generated && (!post || include(IMPLICIT_END, tag))) {
j = 1;
while ((typeof (nx = this.tokens[i + j]) !== "undefined" && (nx = this.tokens[i + j]) !== null) && include(IMPLICIT_END, nx[0])) {
j++;
}
if ((typeof nx !== "undefined" && nx !== null) && nx[0] === ',') {
if (tag === 'TERMINATOR') {
this.tokens.splice(i, 1);
}
} else {
size = close_calls(i);
if (tag !== 'OUTDENT' && include(EXPRESSION_END, tag)) {
stack.pop();
}
size = stack[stack.length - stack_pointer] + 1;
stack[stack.length - stack_pointer] = 0;
return size;
}
}
if (!(prev && IMPLICIT_FUNC.indexOf(prev[0]) >= 0 && IMPLICIT_CALL.indexOf(tag) >= 0)) {
if (tag !== 'OUTDENT' && include(EXPRESSION_END, tag)) {
stack[stack.length - 2] += stack.pop();
return 1;
}
this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
stack[stack.length - 1] += 1;
return 2;
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
return 1;
}, this));
};
// Because our grammar is LALR(1), it can't handle some single-line
// expressions that lack ending delimiters. Use the lexer to add the implicit
// blocks, so it doesn't need to.
// ')' can close a single-line block, but we need to make sure it's balanced.
Rewriter.prototype.add_implicit_indentation = function add_implicit_indentation() {
return this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
var idx, insertion, parens, pre, starter, tok;
if (!(SINGLE_LINERS.indexOf(token[0]) >= 0 && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) {
// 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.
Rewriter.prototype.add_implicit_indentation = function() {
return this.scan_tokens(__bind(function(prev, token, post, i) {
var idx, indent, insertion, outdent, parens, pre, starter, tok;
if (!(include(SINGLE_LINERS, token[0]) && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) {
return 1;
}
starter = token[0];
this.tokens.splice(i + 1, 0, ['INDENT', 2, token[2]]);
indent = ['INDENT', 2, token[2]];
indent.generated = true;
this.tokens.splice(i + 1, 0, indent);
idx = i + 1;
parens = 0;
while (true) {
idx += 1;
tok = this.tokens[idx];
pre = this.tokens[idx - 1];
if ((!tok || (SINGLE_CLOSERS.indexOf(tok[0]) >= 0 && tok[1] !== ';') || (tok[0] === ')' && parens === 0)) && !(starter === 'ELSE' && tok[0] === 'ELSE')) {
if ((!tok || (include(SINGLE_CLOSERS, tok[0]) && tok[1] !== ';') || (tok[0] === ')' && parens === 0)) && !(starter === 'ELSE' && tok[0] === 'ELSE')) {
insertion = pre[0] === "," ? idx - 1 : idx;
this.tokens.splice(insertion, 0, ['OUTDENT', 2, token[2]]);
outdent = ['OUTDENT', 2, token[2]];
outdent.generated = true;
this.tokens.splice(insertion, 0, outdent);
break;
}
if (tok[0] === '(') {
@@ -264,117 +242,153 @@
}
this.tokens.splice(i, 1);
return 0;
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
}, this));
};
// Ensure that all listed pairs of tokens are correctly balanced throughout
// the course of the token stream.
Rewriter.prototype.ensure_balance = function ensure_balance(pairs) {
var _l, _m, key, levels, unclosed, value;
Rewriter.prototype.ensure_balance = function(pairs) {
var _c, _d, key, levels, line, open, open_line, unclosed, value;
levels = {};
this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
var _l, _m, _n, _o, close, open;
_l = pairs;
for (_m = 0, _n = _l.length; _m < _n; _m++) {
pair = _l[_m];
_o = pair;
open = _o[0];
close = _o[1];
open_line = {};
this.scan_tokens(__bind(function(prev, token, post, i) {
var _c, _d, _e, _f, close, open, pair;
_d = pairs;
for (_c = 0, _e = _d.length; _c < _e; _c++) {
pair = _d[_c];
_f = pair;
open = _f[0];
close = _f[1];
levels[open] = levels[open] || 0;
if (token[0] === open) {
if (levels[open] === 0) {
open_line[open] = token[2];
}
levels[open] += 1;
}
if (token[0] === close) {
levels[open] -= 1;
}
if (levels[open] < 0) {
throw new Error("too many " + token[1]);
throw new Error(("too many " + (token[1]) + " on line " + (token[2] + 1)));
}
}
return 1;
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
}, this));
unclosed = (function() {
_l = []; _m = levels;
for (key in _m) { if (__hasProp.call(_m, key)) {
value = _m[key];
if (value > 0) {
_l.push(key);
}
_c = []; _d = levels;
for (key in _d) { if (__hasProp.call(_d, key)) {
value = _d[key];
value > 0 ? _c.push(key) : null;
}}
return _l;
}).call(this);
return _c;
})();
if (unclosed.length) {
throw new Error("unclosed " + unclosed[0]);
open = unclosed[0];
line = open_line[open] + 1;
throw new Error(("unclosed " + open + " on line " + line));
}
};
// We'd like to support syntax like this:
// el.click((event) ->
// el.hide())
// el.click((event) ->
// el.hide())
// In order to accomplish this, move outdents that follow closing parens
// inwards, safely. The steps to accomplish this are:
// 1. Check that all paired tokens are balanced and in order.
// 2. Rewrite the stream with a stack: if you see an '(' or INDENT, add it
// to the stack. If you see an ')' or OUTDENT, pop the stack and replace
// 2. Rewrite the stream with a stack: if you see an `EXPRESSION_START`, add it
// to the stack. If you see an `EXPRESSION_END`, pop the stack and replace
// it with the inverse of what we've just popped.
// 3. Keep track of "debt" for tokens that we fake, to make sure we end
// 3. Keep track of "debt" for tokens that we manufacture, to make sure we end
// up balanced in the end.
Rewriter.prototype.rewrite_closing_parens = function rewrite_closing_parens() {
var _l, debt, key, stack, val;
// 4. Be careful not to alter array or parentheses delimiters with overzealous
// rewriting.
Rewriter.prototype.rewrite_closing_parens = function() {
var _c, debt, key, stack, val;
stack = [];
debt = {};
_l = INVERSES;
for (key in _l) { if (__hasProp.call(_l, key)) {
val = _l[key];
((debt[key] = 0));
_c = INVERSES;
for (key in _c) { if (__hasProp.call(_c, key)) {
val = _c[key];
(debt[key] = 0);
}}
return this.scan_tokens((function(__this) {
var __func = function(prev, token, post, i) {
var inv, match, mtag, tag;
return this.scan_tokens(__bind(function(prev, token, post, i) {
var inv, match, mtag, oppos, tag;
tag = token[0];
inv = INVERSES[token[0]];
// Push openers onto the stack.
if (EXPRESSION_START.indexOf(tag) >= 0) {
if (include(EXPRESSION_START, tag)) {
stack.push(token);
return 1;
// The end of an expression, check stack and debt for a pair.
} else if (EXPRESSION_TAIL.indexOf(tag) >= 0) {
// If the tag is already in our debt, swallow it.
} else if (include(EXPRESSION_END, tag)) {
if (debt[inv] > 0) {
debt[inv] -= 1;
this.tokens.splice(i, 1);
return 0;
} else {
// Pop the stack of open delimiters.
match = stack.pop();
mtag = match[0];
// Continue onwards if it's the expected tag.
if (tag === INVERSES[mtag]) {
return 1;
} else {
// Unexpected close, insert correct close, adding to the debt.
debt[mtag] += 1;
val = mtag === 'INDENT' ? match[1] : INVERSES[mtag];
this.tokens.splice(i, 0, [INVERSES[mtag], val]);
oppos = INVERSES[mtag];
if (tag === oppos) {
return 1;
}
debt[mtag] += 1;
val = [oppos, mtag === 'INDENT' ? match[1] : oppos];
if ((this.tokens[i + 2] == undefined ? undefined : this.tokens[i + 2][0]) === mtag) {
this.tokens.splice(i + 3, 0, val);
stack.push(match);
} else {
this.tokens.splice(i, 0, val);
}
return 1;
}
} else {
return 1;
}
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
}, this));
};
return Rewriter;
}).call(this);
})();
// Constants
// ---------
// List of the token pairs that must be balanced.
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END'], ['SOAKED_INDEX_START', 'SOAKED_INDEX_END']];
// The inverse mappings of `BALANCED_PAIRS` we're trying to fix up, so we can
// look things up from either end.
INVERSES = {};
_d = BALANCED_PAIRS;
for (_c = 0, _e = _d.length; _c < _e; _c++) {
pair = _d[_c];
INVERSES[pair[0]] = pair[1];
INVERSES[pair[1]] = pair[0];
}
// The tokens that signal the start of a balanced pair.
EXPRESSION_START = (function() {
_f = []; _h = BALANCED_PAIRS;
for (_g = 0, _i = _h.length; _g < _i; _g++) {
pair = _h[_g];
_f.push(pair[0]);
}
return _f;
})();
// The tokens that signal the end of a balanced pair.
EXPRESSION_END = (function() {
_j = []; _l = BALANCED_PAIRS;
for (_k = 0, _m = _l.length; _k < _m; _k++) {
pair = _l[_k];
_j.push(pair[1]);
}
return _j;
})();
// Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE = ['CATCH', 'WHEN', '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', '<-', '@'];
// If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'EXTENSION', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT', 'THIS', 'NULL', '@', '->', '=>', '[', '(', '{'];
// Tokens indicating that the implicit call must enclose a block of expressions.
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
// Tokens that always mark the end of an implicit call for single-liners.
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'TERMINATOR', 'INDENT'].concat(EXPRESSION_END);
// Single-line flavors of block expressions that have unclosed endings.
// The grammar can't disambiguate them, so we insert the implicit indentation.
SINGLE_LINERS = ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN'];
SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN'];
})();

View File

@@ -1,52 +1,75 @@
(function(){
var Scope;
var __hasProp = Object.prototype.hasOwnProperty;
// The **Scope** class regulates lexical scoping within CoffeeScript. As you
// generate code, you create a tree of scopes in the same shape as the nested
// function bodies. Each scope knows about the variables declared within it,
// and has a reference to its parent enclosing scope. In this way, we know which
// variables are new and need to be declared with `var`, and which are shared
// with the outside.
// Set up exported variables for both **Node.js** and the browser.
if (!((typeof process !== "undefined" && process !== null))) {
this.exports = this;
}
// Scope objects form a tree corresponding to the shape of the function
// definitions present in the script. They provide lexical scope, to determine
// whether a variable has been seen before or if it needs to be declared.
// Initialize a scope with its parent, for lookups up the chain,
// as well as the Expressions body where it should declare its variables,
// and the function that it wraps.
exports.Scope = (function() {
Scope = function Scope(parent, expressions, method) {
Scope = function(parent, expressions, method) {
var _a;
_a = [parent, expressions, method];
this.parent = _a[0];
this.expressions = _a[1];
this.method = _a[2];
this.variables = {};
this.temp_var = this.parent ? this.parent.temp_var : '_a';
if (this.parent) {
this.temp_var = this.parent.temp_var;
} else {
Scope.root = this;
this.temp_var = '_a';
}
return this;
};
// Look up a variable in lexical scope, or declare it if not found.
Scope.prototype.find = function find(name) {
// The top-level **Scope** object.
Scope.root = null;
// Initialize a scope with its parent, for lookups up the chain,
// as well as a reference to the **Expressions** node is belongs to, which is
// where it should declare its variables, and a reference to the function that
// it wraps.
// Look up a variable name in lexical scope, and declare it if it does not
// already exist.
Scope.prototype.find = function(name) {
if (this.check(name)) {
return true;
}
this.variables[name] = 'var';
return false;
};
// Define a local variable as originating from a parameter in current scope
// -- no var required.
Scope.prototype.parameter = function parameter(name) {
return this.variables[name] = 'param';
// Test variables and return true the first time fn(v, k) returns true
Scope.prototype.any = function(fn) {
var _a, k, v;
_a = this.variables;
for (v in _a) { if (__hasProp.call(_a, v)) {
k = _a[v];
if (fn(v, k)) {
return true;
}
}}
return false;
};
// Just check to see if a variable has already been declared.
Scope.prototype.check = function check(name) {
// Reserve a variable name as originating from a function parameter for this
// scope. No `var` required for internal references.
Scope.prototype.parameter = function(name) {
this.variables[name] = 'param';
return this.variables[name];
};
// Just check to see if a variable has already been declared, without reserving.
Scope.prototype.check = function(name) {
if (this.variables[name]) {
return true;
}
return !!(this.parent && this.parent.check(name));
};
// You can reset a found variable on the immediate scope.
Scope.prototype.reset = function reset(name) {
return delete this.variables[name];
};
// Find an available, short, name for a compiler-generated variable.
Scope.prototype.free_variable = function free_variable() {
// If we need to store an intermediate result, find an available name for a
// compiler-generated variable. `_a`, `_b`, and so on...
Scope.prototype.free_variable = function() {
var ordinal;
while (this.check(this.temp_var)) {
ordinal = 1 + parseInt(this.temp_var.substr(1), 36);
@@ -55,60 +78,58 @@
this.variables[this.temp_var] = 'var';
return this.temp_var;
};
// Ensure that an assignment is made at the top of scope (or top-level
// scope, if requested).
Scope.prototype.assign = function assign(name, value, top_level) {
if (top_level && this.parent) {
return this.parent.assign(name, value, top_level);
}
return this.variables[name] = {
// Ensure that an assignment is made at the top of this scope
// (or at the top-level scope, if requested).
Scope.prototype.assign = function(name, value) {
this.variables[name] = {
value: value,
assigned: true
};
return this.variables[name];
};
// Does this scope reference any variables that need to be declared in the
// given function body?
Scope.prototype.has_declarations = function has_declarations(body) {
return body === this.expressions && this.declared_variables().length;
Scope.prototype.has_declarations = function(body) {
return body === this.expressions && this.any(function(k, val) {
return val === 'var';
});
};
// Does this scope reference any assignments that need to be declared at the
// top of the given function body?
Scope.prototype.has_assignments = function has_assignments(body) {
return body === this.expressions && this.assigned_variables().length;
Scope.prototype.has_assignments = function(body) {
return body === this.expressions && this.any(function(k, val) {
return val.assigned;
});
};
// Return the list of variables first declared in current scope.
Scope.prototype.declared_variables = function declared_variables() {
// Return the list of variables first declared in this scope.
Scope.prototype.declared_variables = function() {
var _a, _b, key, val;
return (function() {
_a = []; _b = this.variables;
for (key in _b) { if (__hasProp.call(_b, key)) {
val = _b[key];
if (val === 'var') {
_a.push(key);
}
val === 'var' ? _a.push(key) : null;
}}
return _a;
}).call(this).sort();
};
// Return the list of variables that are supposed to be assigned at the top
// of scope.
Scope.prototype.assigned_variables = function assigned_variables() {
// Return the list of assignments that are supposed to be made at the top
// of this scope.
Scope.prototype.assigned_variables = function() {
var _a, _b, key, val;
_a = []; _b = this.variables;
for (key in _b) { if (__hasProp.call(_b, key)) {
val = _b[key];
if (val.assigned) {
_a.push(key + ' = ' + val.value);
}
val.assigned ? _a.push(("" + key + " = " + val.value)) : null;
}}
return _a;
};
// Compile the string representing all of the declared variables for this scope.
Scope.prototype.compiled_declarations = function compiled_declarations() {
// Compile the JavaScript for all of the variable declarations in this scope.
Scope.prototype.compiled_declarations = function() {
return this.declared_variables().join(', ');
};
// Compile the string performing all of the variable assignments for this scope.
Scope.prototype.compiled_assignments = function compiled_assignments() {
// Compile the JavaScript for all of the variable assignments in this scope.
Scope.prototype.compiled_assignments = function() {
return this.assigned_variables().join(', ');
};
return Scope;

12
lib/utilities.js Normal file
View File

@@ -0,0 +1,12 @@
(function(){
if (!((typeof process !== "undefined" && process !== null))) {
this.exports = this;
}
exports.utilities = {
extend: "function(child, parent) {\n var ctor = function(){ };\n ctor.prototype = parent.prototype;\n child.__superClass__ = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n }",
bind: "function(func, obj, args) {\n return function() {\n return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);\n };\n }",
range: "function(array, from, to, exclusive) {\n return [\n (from < 0 ? from + array.length : from || 0),\n (to < 0 ? to + array.length : to || array.length) + (exclusive ? 0 : 1)\n ];\n }",
hasProp: 'Object.prototype.hasOwnProperty',
slice: 'Array.prototype.slice'
};
})();

View File

@@ -3,5 +3,5 @@
"description": "Unfancy JavaScript",
"keywords": ["javascript", "language"],
"author": "Jeremy Ashkenas",
"version": "0.5.4"
"version": "0.6.2"
}

View File

@@ -1,57 +1,70 @@
# `cake` is a simplified version of Make (Rake, Jake) for CoffeeScript.
# You define tasks with names and descriptions in a Cakefile, and can call them
# from the command line, or invoke them from other tasks.
# `cake` is a simplified version of [Make](http://www.gnu.org/software/make/)
# ([Rake](http://rake.rubyforge.org/), [Jake](http://github.com/280north/jake))
# for CoffeeScript. You define tasks with names and descriptions in a Cakefile,
# and can call them from the command line, or invoke them from other tasks.
#
# Running `cake` with no arguments will print out a list of all the tasks in the
# current directory's Cakefile.
fs: require 'fs'
path: require 'path'
coffee: require 'coffee-script'
optparse: require 'optparse'
# External dependencies.
fs: require 'fs'
path: require 'path'
helpers: require('./helpers').helpers
optparse: require './optparse'
CoffeeScript: require './coffee-script'
# Keep track of the list of defined tasks, the accepted options, and so on.
tasks: {}
options: {}
switches: []
oparse: null
# Mixin the top-level Cake functions for Cakefiles to use.
process.mixin {
# Mixin the top-level Cake functions for Cakefiles to use directly.
helpers.extend global, {
# Define a task with a name, a description, and the action itself.
# Define a Cake task with a short name, an optional sentence description,
# and the function to run as the action itself.
task: (name, description, action) ->
[action, description]: [description, action] unless action
tasks[name]: {name: name, description: description, action: action}
# Define an option that the Cakefile accepts.
# Define an option that the Cakefile accepts. The parsed options hash,
# containing all of the command-line options passed, will be made available
# as the first argument to the action.
option: (letter, flag, description) ->
switches.push [letter, flag, description]
# Invoke another task in the Cakefile.
# Invoke another task in the current Cakefile.
invoke: (name) ->
no_such_task name unless tasks[name]
tasks[name].action(options)
tasks[name].action options
}
# Running `cake` runs the tasks you pass asynchronously (node-style), or
# prints them out, with no arguments.
# Run `cake`. Executes all of the tasks you pass, in order. Note that Node's
# asynchrony may cause tasks to execute in a different order than you'd expect.
# If no tasks are passed, print the help screen.
exports.run: ->
path.exists 'Cakefile', (exists) ->
throw new Error('Cakefile not found in ' + process.cwd()) unless exists
args: process.ARGV[2...process.ARGV.length]
eval coffee.compile fs.readFileSync 'Cakefile'
throw new Error("Cakefile not found in ${process.cwd()}") unless exists
args: process.argv[2...process.argv.length]
CoffeeScript.run fs.readFileSync('Cakefile').toString(), {source: 'Cakefile'}
oparse: new optparse.OptionParser switches
return print_tasks() unless args.length
options: oparse.parse(args)
invoke arg for arg in options.arguments
# Display the list of Cake tasks.
# Display the list of Cake tasks in a format similar to `rake -T`
print_tasks: ->
puts ''
for name, task of tasks
spaces: 20 - name.length
spaces: if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
puts "cake " + name + spaces + ' # ' + task.description
puts '\n' + oparse.help() + '\n' if switches.length
desc: if task.description then "# $task.description" else ''
puts "cake $name$spaces $desc"
puts oparse.help() if switches.length
# Print an error and exit when attempting to all an undefined task.
no_such_task: (task) ->
process.stdio.writeError('No such task: "' + task + '"\n')
process.exit(1)
puts "No such task: \"$task\""
process.exit 1

View File

@@ -1,19 +1,74 @@
# Set up for both the browser and the server.
if process?
process.mixin require 'nodes'
path: require 'path'
lexer: new (require('lexer').Lexer)()
parser: require('parser').parser
else
lexer: new Lexer()
parser: exports.parser
this.exports: this.CoffeeScript: {}
# CoffeeScript can be used both on the server, as a command-line compiler based
# on Node.js/V8, or to run CoffeeScripts directly in the browser. This module
# contains the main entry functions for tokenzing, parsing, and compiling source
# CoffeeScript into JavaScript.
#
# If included on a webpage, it will automatically sniff out, compile, and
# execute all scripts present in `text/coffeescript` tags.
# Thin wrapper for Jison compatibility around the real lexer.
# Set up dependencies correctly for both the server and the browser.
if process?
path: require 'path'
Lexer: require('./lexer').Lexer
parser: require('./parser').parser
helpers: require('./helpers').helpers
helpers.extend global, require './nodes'
if require.registerExtension
require.registerExtension '.coffee', (content) -> compile content
else
this.exports: this.CoffeeScript: {}
Lexer: this.Lexer
parser: this.parser
helpers: this.helpers
# The current CoffeeScript version number.
exports.VERSION: '0.6.2'
# Instantiate a Lexer for our use here.
lexer: new Lexer()
# Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison
# compiler.
exports.compile: compile: (code, options) ->
options: or {}
try
(parser.parse lexer.tokenize code).compile options
catch err
err.message: "In $options.source, $err.message" if options.source
throw err
# Tokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens: (code) ->
lexer.tokenize code
# Tokenize and parse a string of CoffeeScript code, and return the AST. You can
# then compile it by calling `.compile()` on the root, or traverse it by using
# `.traverse()` with a callback.
exports.nodes: (code) ->
parser.parse lexer.tokenize code
# Compile and execute a string of CoffeeScript (on the server), correctly
# setting `__filename`, `__dirname`, and relative `require()`.
exports.run: ((code, options) ->
module.filename: __filename: options.source
__dirname: path.dirname(__filename)
eval exports.compile code, options
)
# Extend CoffeeScript with a custom language extension. It should hook in to
# the **Lexer** (as a peer of any of the lexer's tokenizing methods), and
# push a token on to the stack that contains a **Node** as the value (as a
# peer of the nodes in [nodes.coffee](nodes.html)).
exports.extend: (func) ->
Lexer.extensions.push func
# The real Lexer produces a generic stream of tokens. This object provides a
# thin wrapper around it, compatible with the Jison API. We can then pass it
# directly as a "Jison lexer".
parser.lexer: {
lex: ->
token: @tokens[@pos] or [""]
@pos += 1
@pos: + 1
this.yylineno: token[2]
this.yytext: token[1]
token[0]
@@ -24,22 +79,10 @@ parser.lexer: {
showPosition: -> @pos
}
exports.VERSION: '0.5.4'
# Compile CoffeeScript to JavaScript, using the Coffee/Jison compiler.
exports.compile: (code, options) ->
(parser.parse lexer.tokenize code).compile(options)
# Just the tokens.
exports.tokens: (code) ->
lexer.tokenize code
# Just the nodes.
exports.nodes: (code) ->
parser.parse lexer.tokenize code
# Activate CoffeeScript in the browser by having it compile and eval
# all script tags with a content-type of text/coffeescript.
# Activate CoffeeScript in the browser by having it compile and evaluate
# all script tags with a content-type of `text/coffeescript`. This happens
# on page load. Unfortunately, the text contents of remote scripts cannot be
# accessed from the browser, so only inline script tags will work.
if document? and document.getElementsByTagName
process_scripts: ->
for tag in document.getElementsByTagName('script') when tag.type is 'text/coffeescript'

172
src/command.coffee Normal file
View File

@@ -0,0 +1,172 @@
# The `coffee` utility. Handles command-line compilation of CoffeeScript
# into various forms: saved into `.js` files or printed to stdout, piped to
# [JSLint](http://javascriptlint.com/) or recompiled every time the source is
# saved, printed as a token stream or as the syntax tree, or launch an
# interactive REPL.
# External dependencies.
fs: require 'fs'
path: require 'path'
optparse: require './optparse'
CoffeeScript: require './coffee-script'
{spawn, exec}: require('child_process')
# The help banner that is printed when `coffee` is called without arguments.
BANNER: '''
coffee compiles CoffeeScript source files into JavaScript.
Usage:
coffee path/to/script.coffee
'''
# The list of all the valid option flags that `coffee` knows how to handle.
SWITCHES: [
['-c', '--compile', 'compile to JavaScript and save as .js files']
['-i', '--interactive', 'run an interactive CoffeeScript REPL']
['-o', '--output [DIR]', 'set the directory for compiled JavaScript']
['-w', '--watch', 'watch scripts for changes, and recompile']
['-p', '--print', 'print the compiled JavaScript to stdout']
['-l', '--lint', 'pipe the compiled JavaScript through JSLint']
['-s', '--stdio', 'listen for and compile scripts over stdio']
['-e', '--eval', 'compile a string from the command line']
[ '--no-wrap', 'compile without the top-level function wrapper']
['-t', '--tokens', 'print the tokens that the lexer produces']
['-n', '--nodes', 'print the parse tree that Jison produces']
['-v', '--version', 'display CoffeeScript version']
['-h', '--help', 'display this help message']
]
# Top-level objects shared by all the functions.
options: {}
sources: []
option_parser: null
# Run `coffee` by parsing passed options and determining what action to take.
# Many flags cause us to divert before compiling anything. Flags passed after
# `--` will be passed verbatim to your script as arguments in `process.argv`
exports.run: ->
parse_options()
return usage() if options.help
return version() if options.version
return require './repl' if options.interactive
return compile_stdio() if options.stdio
return compile_script 'console', sources[0] if options.eval
return usage() unless sources.length
separator: sources.indexOf '--'
flags: []
if separator >= 0
flags: sources[(separator + 1)...sources.length]
sources: sources[0...separator]
process.ARGV: process.argv: flags
compile_scripts()
# Asynchronously read in each CoffeeScript in a list of source files and
# compile them. If a directory is passed, recursively compile all source
# files in it and all subdirectories.
compile_scripts: ->
for source in sources
base: source
compile: (source) ->
path.exists source, (exists) ->
throw new Error "File not found: $source" unless exists
fs.stat source, (err, stats) ->
if stats.isDirectory()
fs.readdir source, (err, files) ->
for file in files
compile path.join(source, file)
else if path.extname(source) is '.coffee'
fs.readFile source, (err, code) -> compile_script(source, code.toString(), base)
watch source, base if options.watch
compile source
# 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.
compile_script: (source, code, base) ->
o: options
code_opts: compile_options source
try
if o.tokens then print_tokens CoffeeScript.tokens code
else if o.nodes then puts CoffeeScript.nodes(code).toString()
else if o.run then CoffeeScript.run code, code_opts
else
js: CoffeeScript.compile code, code_opts
if o.print then print js
else if o.compile then write_js source, js, base
else if o.lint then lint js
catch err
if o.watch then puts err.message else throw err
# Attach the appropriate listeners to compile scripts incoming over **stdin**,
# and write them back to **stdout**.
compile_stdio: ->
code: ''
stdin: process.openStdin()
stdin.addListener 'data', (buffer) ->
code: + buffer.toString() if buffer
stdin.addListener 'end', ->
compile_script 'stdio', code
# Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every
# time the file is updated. May be used in combination with other options,
# such as `--lint` or `--print`.
watch: (source, base) ->
fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
return if curr.mtime.getTime() is prev.mtime.getTime()
puts "Compiled $source" if options.compile
fs.readFile source, (err, code) -> compile_script(source, code.toString(), base)
# Write out a JavaScript source file with the compiled code. By default, files
# are written out in `cwd` as `.js` files with the same name, but the output
# directory can be customized with `--output`.
write_js: (source, js, base) ->
filename: path.basename(source, path.extname(source)) + '.js'
src_dir: path.dirname source
base_dir: src_dir.substring base.length
dir: if options.output then path.join options.output, base_dir else src_dir
js_path: path.join dir, filename
compile: -> fs.writeFile js_path, js
path.exists dir, (exists) ->
if exists then compile() else exec "mkdir -p $dir", compile
# Pipe compiled JS through JSLint (requires a working `jsl` command), printing
# any errors or warnings that arise.
lint: (js) ->
print_it: (buffer) -> print buffer.toString()
jsl: spawn 'jsl', ['-nologo', '-stdin']
jsl.stdout.addListener 'data', print_it
jsl.stderr.addListener 'data', print_it
jsl.stdin.write js
jsl.stdin.end()
# Pretty-print a stream of tokens.
print_tokens: (tokens) ->
strings: for token in tokens
[tag, value]: [token[0], token[1].toString().replace(/\n/, '\\n')]
"[$tag $value]"
puts strings.join(' ')
# Use the [OptionParser module](optparse.html) to extract all options from
# `process.argv` that are specified in `SWITCHES`.
parse_options: ->
option_parser: new optparse.OptionParser SWITCHES, BANNER
o: options: option_parser.parse(process.argv)
options.run: not (o.compile or o.print or o.lint)
options.print: !! (o.print or (o.eval or o.stdio and o.compile))
sources: options.arguments[2...options.arguments.length]
# The compile-time options to pass to the CoffeeScript compiler.
compile_options: (source) ->
o: {source: source}
o['no_wrap']: options['no-wrap']
o
# Print the `--help` usage message and exit.
usage: ->
puts option_parser.help()
process.exit 0
# Print the `--version` message and exit.
version: ->
puts "CoffeeScript version $CoffeeScript.VERSION"
process.exit 0

View File

@@ -1,139 +0,0 @@
fs: require 'fs'
path: require 'path'
optparse: require 'optparse'
CoffeeScript: require 'coffee-script'
BANNER: '''
coffee compiles CoffeeScript source files into JavaScript.
Usage:
coffee path/to/script.coffee
'''
SWITCHES: [
['-c', '--compile', 'compile to JavaScript and save as .js files']
['-i', '--interactive', 'run an interactive CoffeeScript REPL']
['-o', '--output [DIR]', 'set the directory for compiled JavaScript']
['-w', '--watch', 'watch scripts for changes, and recompile']
['-p', '--print', 'print the compiled JavaScript to stdout']
['-l', '--lint', 'pipe the compiled JavaScript through JSLint']
['-s', '--stdio', 'listen for and compile scripts over stdio']
['-e', '--eval', 'compile a string from the command line']
[ '--no-wrap', 'compile without the top-level function wrapper']
['-t', '--tokens', 'print the tokens that the lexer produces']
['-n', '--nodes', 'print the parse tree that Jison produces']
['-v', '--version', 'display CoffeeScript version']
['-h', '--help', 'display this help message']
]
options: {}
sources: []
option_parser: null
# The CommandLine handles all of the functionality of the `coffee` utility.
exports.run: ->
parse_options()
return usage() if options.help
return version() if options.version
return require 'repl' if options.interactive
return compile_stdio() if options.stdio
return compile_script 'unknown', sources[0] if options.eval
return usage() unless sources.length
separator: sources.indexOf '--'
flags: []
if separator >= 0
flags: sources[(separator + 1)...sources.length]
sources: sources[0...separator]
process.ARGV = flags
watch_scripts() if options.watch
compile_scripts()
this
# The "--help" usage message.
usage: ->
puts '\n' + option_parser.help() + '\n'
process.exit 0
# The "--version" message.
version: ->
puts "CoffeeScript version " + CoffeeScript.VERSION
process.exit 0
# Compiles the source CoffeeScript, returning the desired JavaScript, tokens,
# or JSLint results.
compile_scripts: ->
compile: (source) ->
path.exists source, (exists) ->
throw new Error 'File not found: ' + source unless exists
fs.readFile source, (err, code) -> compile_script(source, code)
compile(source) for source in sources
# Compile a single source script, containing the given code, according to the
# requested options. Both compile_scripts and watch_scripts share this method.
compile_script: (source, code) ->
o: options
try
if o.tokens then print_tokens CoffeeScript.tokens code
else if o.nodes then puts CoffeeScript.nodes(code).toString()
else
js: CoffeeScript.compile code, compile_options()
if o.compile then write_js source, js
else if o.lint then lint js
else if o.print or o.eval then print js
else
__filename: source
__dirname: path.dirname source
eval js
catch err
if o.watch then puts err.message else throw err
# Listen for and compile scripts over stdio.
compile_stdio: ->
code: ''
process.stdio.open()
process.stdio.addListener 'data', (string) ->
code += string if string
process.stdio.addListener 'close', ->
process.stdio.write CoffeeScript.compile code, compile_options()
# Watch a list of source CoffeeScript files, recompiling them every time the
# files are updated.
watch_scripts: ->
watch: (source) ->
fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
return if curr.mtime.getTime() is prev.mtime.getTime()
fs.readFile source, (err, code) -> compile_script(source, code)
watch(source) for source in sources
# Write out a JavaScript source file with the compiled code.
write_js: (source, js) ->
filename: path.basename(source, path.extname(source)) + '.js'
dir: options.output or path.dirname(source)
js_path: path.join dir, filename
fs.writeFile js_path, js
# Pipe compiled JS through JSLint (requires a working 'jsl' command).
lint: (js) ->
jsl: process.createChildProcess('jsl', ['-nologo', '-stdin'])
jsl.addListener 'output', (result) ->
puts result.replace(/\n/g, '') if result
jsl.addListener 'error', (result) ->
puts result if result
jsl.write js
jsl.close()
# Pretty-print a token stream.
print_tokens: (tokens) ->
strings: for token in tokens
'[' + token[0] + ' ' + token[1].toString().replace(/\n/, '\\n') + ']'
puts strings.join(' ')
# Use OptionParser for all the options.
parse_options: ->
option_parser: new optparse.OptionParser SWITCHES, BANNER
options: option_parser.parse(process.ARGV)
sources: options.arguments[2...options.arguments.length]
# The options to pass to the CoffeeScript compiler.
compile_options: ->
if options['no-wrap'] then {no_wrap: true} else {}

View File

@@ -1,73 +1,98 @@
# The CoffeeScript parser is generated by [Jison](http://github.com/zaach/jison)
# from this grammar file. Jison is a bottom-up parser generator, similar in
# style to [Bison](http://www.gnu.org/software/bison), implemented in JavaScript.
# It can recognize [LALR(1), LR(0), SLR(1), and LR(1)](http://en.wikipedia.org/wiki/LR_grammar)
# type grammars. To create the Jison parser, we list the pattern to match
# on the left-hand side, and the action to take (usually the creation of syntax
# tree nodes) on the right. As the parser runs, it
# shifts tokens from our token stream, from left to right, and
# [attempts to match](http://en.wikipedia.org/wiki/Bottom-up_parsing)
# the token sequence against the rules below. When a match can be made, it
# reduces into the [nonterminal](http://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols)
# (the enclosing name at the top), and we proceed from there.
#
# If you run the `cake build:parser` command, Jison constructs a parse table
# from our rules and saves it into `lib/parser.js`.
# The only dependency is on the **Jison.Parser**.
Parser: require('jison').Parser
# DSL ===================================================================
# Jison DSL
# ---------
# Detect functions: [
# Since we're going to be wrapped in a function by Jison in any case, if our
# action immediately returns a value, we can optimize by removing the function
# wrapper and just returning the value directly.
unwrap: /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
# Quickie DSL for Jison access.
o: (pattern_string, func, options) ->
if func
func: if match: (func + "").match(unwrap) then match[1] else '(' + func + '())'
[pattern_string, '$$ = ' + func + ';', options]
else
[pattern_string, '$$ = $1;', options]
# Our handy DSL for Jison grammar generation, thanks to
# [Tim Caswell](http://github.com/creationix). For every rule in the grammar,
# we pass the pattern-defining string, the action to run, and extra options,
# optionally. If no action is specified, we simply pass the value of the
# previous nonterminal.
o: (pattern_string, action, options) ->
return [pattern_string, '$$ = $1;', options] unless action
action: if match: (action + '').match(unwrap) then match[1] else "($action())"
[pattern_string, "$$ = $action;", options]
# Precedence ===========================================================
operators: [
["left", '?']
["nonassoc", 'UMINUS', 'UPLUS', 'NOT', '!', '!!', '~', '++', '--']
["left", '*', '/', '%']
["left", '+', '-']
["left", '<<', '>>', '>>>']
["left", '&', '|', '^']
["left", '<=', '<', '>', '>=']
["right", 'DELETE', 'INSTANCEOF', 'TYPEOF']
["right", '==', '!=', 'IS', 'ISNT']
["left", '&&', '||', 'AND', 'OR']
["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=']
["left", '.']
["right", 'INDENT']
["left", 'OUTDENT']
["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW']
["right", 'FOR', 'NEW', 'SUPER', 'CLASS']
["left", 'EXTENDS']
["right", 'ASSIGN', 'RETURN']
["right", '->', '=>', 'UNLESS', 'IF', 'ELSE', 'WHILE']
]
# Grammar ==============================================================
# Grammatical Rules
# -----------------
# In all of the rules that follow, you'll see the name of the nonterminal as
# the key to a list of alternative matches. With each match's action, the
# dollar-sign variables are provided by Jison as references to the value of
# their numeric position, so in this rule:
#
# "Expression UNLESS Expression"
#
# `$1` would be the value of the first `Expression`, `$2` would be the token
# for the `UNLESS` terminal, and `$3` would be the value of the second
# `Expression`.
grammar: {
# All parsing will end in this rule, being the trunk of the AST.
# The **Root** is the top-level node in the syntax tree. Since we parse bottom-up,
# all parsing must end here.
Root: [
o "", -> new Expressions()
o "TERMINATOR", -> new Expressions()
o "Expressions", -> $1
o "Block TERMINATOR", -> $1
o "", -> new Expressions()
o "TERMINATOR", -> new Expressions()
o "Body"
o "Block TERMINATOR"
]
# Any list of expressions or method body, seperated by line breaks or semis.
Expressions: [
o "Expression", -> Expressions.wrap([$1])
o "Expressions TERMINATOR Expression", -> $1.push($3)
o "Expressions TERMINATOR", -> $1
# Any list of statements and expressions, seperated by line breaks or semicolons.
Body: [
o "Line", -> Expressions.wrap [$1]
o "Body TERMINATOR Line", -> $1.push $3
o "Body TERMINATOR"
]
# All types of expressions in our language. The basic unit of CoffeeScript
# is the expression.
# Expressions and statements, which make up a line in a body.
Line: [
o "Expression"
o "Statement"
]
# Pure statements which cannot be expressions.
Statement: [
o "Return"
o "Throw"
o "BREAK", -> new LiteralNode $1
o "CONTINUE", -> new LiteralNode $1
]
# All the different types of expressions in our language. The basic unit of
# CoffeeScript is the **Expression** -- everything that can be an expression
# is one. Expressions serve as the building blocks of many other rules, making
# them somewhat circular.
Expression: [
o "Value"
o "Call"
o "Curry"
o "Code"
o "Operation"
o "Assign"
o "If"
o "Try"
o "Throw"
o "Return"
o "While"
o "For"
o "Switch"
@@ -76,265 +101,264 @@ grammar: {
o "Splat"
o "Existence"
o "Comment"
o "Extension"
]
# A block of expressions. Note that the Rewriter will convert some postfix
# forms into blocks for us, by altering the token stream.
# A an indented block of expressions. Note that the [Rewriter](rewriter.html)
# will convert some postfix forms into blocks for us, by adjusting the
# token stream.
Block: [
o "INDENT Expressions OUTDENT", -> $2
o "INDENT Body OUTDENT", -> $2
o "INDENT OUTDENT", -> new Expressions()
o "TERMINATOR Comment", -> Expressions.wrap [$2]
]
# A literal identifier, a variable name or property.
Identifier: [
o "IDENTIFIER", -> new LiteralNode(yytext)
o "IDENTIFIER", -> new LiteralNode $1
]
# Alphanumerics are separated from the other **Literal** matchers because
# they can also serve as keys in object literals.
AlphaNumeric: [
o "NUMBER", -> new LiteralNode(yytext)
o "STRING", -> new LiteralNode(yytext)
o "NUMBER", -> new LiteralNode $1
o "STRING", -> new LiteralNode $1
]
# All hard-coded values. These can be printed straight to JavaScript.
# All of our immediate values. These can (in general), be passed straight
# through and printed to JavaScript.
Literal: [
o "AlphaNumeric", -> $1
o "JS", -> new LiteralNode(yytext)
o "REGEX", -> new LiteralNode(yytext)
o "BREAK", -> new LiteralNode(yytext)
o "CONTINUE", -> new LiteralNode(yytext)
o "TRUE", -> new LiteralNode(true)
o "FALSE", -> new LiteralNode(false)
o "YES", -> new LiteralNode(true)
o "NO", -> new LiteralNode(false)
o "ON", -> new LiteralNode(true)
o "OFF", -> new LiteralNode(false)
o "AlphaNumeric"
o "JS", -> new LiteralNode $1
o "REGEX", -> new LiteralNode $1
o "TRUE", -> new LiteralNode true
o "FALSE", -> new LiteralNode false
o "YES", -> new LiteralNode true
o "NO", -> new LiteralNode false
o "ON", -> new LiteralNode true
o "OFF", -> new LiteralNode false
]
# Assignment to a variable (or index).
# Assignment of a variable, property, or index to a value.
Assign: [
o "Value ASSIGN Expression", -> new AssignNode($1, $3)
o "Assignable ASSIGN Expression", -> new AssignNode $1, $3
]
# Assignment within an object literal (can be quoted).
# Assignment when it happens within an object literal. The difference from
# the ordinary **Assign** is that these allow numbers and strings as keys.
AssignObj: [
o "Identifier ASSIGN Expression", -> new AssignNode(new ValueNode($1), $3, 'object')
o "AlphaNumeric ASSIGN Expression", -> new AssignNode(new ValueNode($1), $3, 'object')
o "Identifier", -> new ValueNode $1
o "AlphaNumeric"
o "Identifier ASSIGN Expression", -> new AssignNode new ValueNode($1), $3, 'object'
o "AlphaNumeric ASSIGN Expression", -> new AssignNode new ValueNode($1), $3, 'object'
o "Comment"
]
# A return statement.
# A return statement from a function body.
Return: [
o "RETURN Expression", -> new ReturnNode($2)
o "RETURN", -> new ReturnNode(new ValueNode(new LiteralNode('null')))
o "RETURN Expression", -> new ReturnNode $2
o "RETURN", -> new ReturnNode new ValueNode new LiteralNode 'null'
]
# A comment.
# A comment. Because CoffeeScript passes comments through to JavaScript, we
# have to parse comments like any other construct, and identify all of the
# positions in which they can occur in the grammar.
Comment: [
o "COMMENT", -> new CommentNode(yytext)
o "COMMENT", -> new CommentNode $1
o "HERECOMMENT", -> new CommentNode $1, 'herecomment'
]
# Arithmetic and logical operators
# For Ruby's Operator precedence, see: [
# https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
Operation: [
o "! Expression", -> new OpNode('!', $2)
o "!! Expression", -> new OpNode('!!', $2)
o("- Expression", (-> new OpNode('-', $2)), {prec: 'UMINUS'})
o("+ Expression", (-> new OpNode('+', $2)), {prec: 'UPLUS'})
o "NOT Expression", -> new OpNode('not', $2)
o "~ Expression", -> new OpNode('~', $2)
o "-- Expression", -> new OpNode('--', $2)
o "++ Expression", -> new OpNode('++', $2)
o "DELETE Expression", -> new OpNode('delete', $2)
o "TYPEOF Expression", -> new OpNode('typeof', $2)
o "Expression --", -> new OpNode('--', $1, null, true)
o "Expression ++", -> new OpNode('++', $1, null, true)
o "Expression * Expression", -> new OpNode('*', $1, $3)
o "Expression / Expression", -> new OpNode('/', $1, $3)
o "Expression % Expression", -> new OpNode('%', $1, $3)
o "Expression + Expression", -> new OpNode('+', $1, $3)
o "Expression - Expression", -> new OpNode('-', $1, $3)
o "Expression << Expression", -> new OpNode('<<', $1, $3)
o "Expression >> Expression", -> new OpNode('>>', $1, $3)
o "Expression >>> Expression", -> new OpNode('>>>', $1, $3)
o "Expression & Expression", -> new OpNode('&', $1, $3)
o "Expression | Expression", -> new OpNode('|', $1, $3)
o "Expression ^ Expression", -> new OpNode('^', $1, $3)
o "Expression <= Expression", -> new OpNode('<=', $1, $3)
o "Expression < Expression", -> new OpNode('<', $1, $3)
o "Expression > Expression", -> new OpNode('>', $1, $3)
o "Expression >= Expression", -> new OpNode('>=', $1, $3)
o "Expression == Expression", -> new OpNode('==', $1, $3)
o "Expression != Expression", -> new OpNode('!=', $1, $3)
o "Expression IS Expression", -> new OpNode('is', $1, $3)
o "Expression ISNT Expression", -> new OpNode('isnt', $1, $3)
o "Expression && Expression", -> new OpNode('&&', $1, $3)
o "Expression || Expression", -> new OpNode('||', $1, $3)
o "Expression AND Expression", -> new OpNode('and', $1, $3)
o "Expression OR Expression", -> new OpNode('or', $1, $3)
o "Expression ? Expression", -> new OpNode('?', $1, $3)
o "Expression -= Expression", -> new OpNode('-=', $1, $3)
o "Expression += Expression", -> new OpNode('+=', $1, $3)
o "Expression /= Expression", -> new OpNode('/=', $1, $3)
o "Expression *= Expression", -> new OpNode('*=', $1, $3)
o "Expression %= Expression", -> new OpNode('%=', $1, $3)
o "Expression ||= Expression", -> new OpNode('||=', $1, $3)
o "Expression &&= Expression", -> new OpNode('&&=', $1, $3)
o "Expression ?= Expression", -> new OpNode('?=', $1, $3)
o "Expression INSTANCEOF Expression", -> new OpNode('instanceof', $1, $3)
o "Expression IN Expression", -> new OpNode('in', $1, $3)
]
# The existence operator.
# [The existential operator](http://jashkenas.github.com/coffee-script/#existence).
Existence: [
o "Expression ?", -> new ExistenceNode($1)
o "Expression ?", -> new ExistenceNode $1
]
# Function definition.
# The **Code** node is the function literal. It's defined by an indented block
# of **Expressions** preceded by a function arrow, with an optional parameter
# list.
Code: [
o "PARAM_START ParamList PARAM_END FuncGlyph Block", -> new CodeNode($2, $5, $4)
o "FuncGlyph Block", -> new CodeNode([], $2, $1)
o "PARAM_START ParamList PARAM_END FuncGlyph Block", -> new CodeNode $2, $5, $4
o "FuncGlyph Block", -> new CodeNode [], $2, $1
]
# The symbols to signify functions, and bound functions.
# CoffeeScript has two different symbols for functions. `->` is for ordinary
# functions, and `=>` is for functions bound to the current value of *this*.
FuncGlyph: [
o "->", -> 'func'
o "=>", -> 'boundfunc'
]
# The parameters to a function definition.
# An optional, trailing comma.
OptComma: [
o ''
o ','
]
# The list of parameters that a function accepts can be of any length.
ParamList: [
o "", -> []
o "Param", -> [$1]
o "ParamList , Param", -> $1.concat [$3]
]
# A Parameter (or ParamSplat) in a function definition.
# A single parameter in a function definition can be ordinary, or a splat
# that hoovers up the remaining arguments.
Param: [
o "PARAM", -> new LiteralNode(yytext)
o "Param . . .", -> new SplatNode($1)
o "PARAM", -> new LiteralNode $1
o "Param . . .", -> new SplatNode $1
]
# A regular splat.
# A splat that occurs outside of a parameter list.
Splat: [
o "Expression . . .", -> new SplatNode($1)
o "Expression . . .", -> new SplatNode $1
]
# Expressions that can be treated as values.
# Variables and properties that can be assigned to.
SimpleAssignable: [
o "Identifier", -> new ValueNode $1
o "Value Accessor", -> $1.push $2
o "Invocation Accessor", -> new ValueNode $1, [$2]
o "ThisProperty"
]
# Everything that can be assigned to.
Assignable: [
o "SimpleAssignable"
o "Array", -> new ValueNode $1
o "Object", -> new ValueNode $1
]
# The types of things that can be treated as values -- assigned to, invoked
# as functions, indexed into, named as a class, etc.
Value: [
o "Identifier", -> new ValueNode($1)
o "Literal", -> new ValueNode($1)
o "Array", -> new ValueNode($1)
o "Object", -> new ValueNode($1)
o "Parenthetical", -> new ValueNode($1)
o "Range", -> new ValueNode($1)
o "This", -> $1
o "Value Accessor", -> $1.push($2)
o "Invocation Accessor", -> new ValueNode($1, [$2])
o "Assignable"
o "Literal", -> new ValueNode $1
o "Parenthetical", -> new ValueNode $1
o "Range", -> new ValueNode $1
o "This"
o "NULL", -> new ValueNode new LiteralNode 'null'
]
# Accessing into an object or array, through dot or index notation.
# The general group of accessors into an object, by property, by prototype
# or by array index or slice.
Accessor: [
o "PROPERTY_ACCESS Identifier", -> new AccessorNode($2)
o "PROTOTYPE_ACCESS Identifier", -> new AccessorNode($2, 'prototype')
o "SOAK_ACCESS Identifier", -> new AccessorNode($2, 'soak')
o "PROPERTY_ACCESS Identifier", -> new AccessorNode $2
o "PROTOTYPE_ACCESS Identifier", -> new AccessorNode $2, 'prototype'
o "::", -> new AccessorNode(new LiteralNode('prototype'))
o "SOAK_ACCESS Identifier", -> new AccessorNode $2, 'soak'
o "Index"
o "Slice", -> new SliceNode($1)
o "Slice", -> new SliceNode $1
]
# Indexing into an object or array.
# Indexing into an object or array using bracket notation.
Index: [
o "INDEX_START Expression INDEX_END", -> new IndexNode($2)
o "SOAKED_INDEX_START Expression SOAKED_INDEX_END", -> new IndexNode($2, 'soak')
o "INDEX_START Expression INDEX_END", -> new IndexNode $2
o "SOAKED_INDEX_START Expression SOAKED_INDEX_END", -> new IndexNode $2, 'soak'
]
# An object literal.
# In CoffeeScript, an object literal is simply a list of assignments.
Object: [
o "{ AssignList }", -> new ObjectNode($2)
o "{ IndentedAssignList }", -> new ObjectNode($2)
o "{ AssignList OptComma }", -> new ObjectNode $2
]
# A class literal.
Class: [
o "CLASS Value", -> new ClassNode($2)
o "CLASS Value EXTENDS Value", -> new ClassNode($2, $4)
o "CLASS Value IndentedAssignList", -> new ClassNode($2, null, $3)
o "CLASS Value EXTENDS Value IndentedAssignList", -> new ClassNode($2, $4, $5)
]
# Assignment within an object literal (comma or newline separated).
# Assignment of properties within an object literal can be separated by
# comma, as in JavaScript, or simply by newline.
AssignList: [
o "", -> []
o "AssignObj", -> [$1]
o "AssignList , AssignObj", -> $1.concat [$3]
o "AssignList TERMINATOR AssignObj", -> $1.concat [$3]
o "AssignList , TERMINATOR AssignObj", -> $1.concat [$4]
o "AssignList OptComma TERMINATOR AssignObj", -> $1.concat [$4]
o "AssignList OptComma INDENT AssignList OptComma OUTDENT", -> $1.concat $4
]
# A list of assignments in a block indentation.
IndentedAssignList: [
o "INDENT AssignList OUTDENT", -> $2
# Class definitions have optional bodies of prototype property assignments,
# and optional references to the superclass.
Class: [
o "CLASS SimpleAssignable", -> new ClassNode $2
o "CLASS SimpleAssignable EXTENDS Value", -> new ClassNode $2, $4
o "CLASS SimpleAssignable INDENT ClassBody OUTDENT", -> new ClassNode $2, null, $4
o "CLASS SimpleAssignable EXTENDS Value INDENT ClassBody OUTDENT", -> new ClassNode $2, $4, $6
]
# All flavors of function call (instantiation, super, and regular).
# Assignments that can happen directly inside a class declaration.
ClassAssign: [
o "AssignObj", -> $1
o "ThisProperty ASSIGN Expression", -> new AssignNode new ValueNode($1), $3, 'this'
]
# A list of assignments to a class.
ClassBody: [
o "", -> []
o "ClassAssign", -> [$1]
o "ClassBody TERMINATOR ClassAssign", -> $1.concat $3
]
# The three flavors of function call: normal, object instantiation with `new`,
# and calling `super()`
Call: [
o "Invocation", -> $1
o "Invocation"
o "NEW Invocation", -> $2.new_instance()
o "Super", -> $1
o "Super"
]
# Extending an object's prototype.
# Binds a function call to a context and/or arguments.
Curry: [
o "Value <- Arguments", -> new CurryNode $1, $3
]
# Extending an object by setting its prototype chain to reference a parent
# object.
Extends: [
o "Value EXTENDS Value", -> new ExtendsNode($1, $3)
o "SimpleAssignable EXTENDS Value", -> new ExtendsNode $1, $3
]
# A generic function invocation.
# Ordinary function invocation, or a chained series of calls.
Invocation: [
o "Value Arguments", -> new CallNode($1, $2)
o "Invocation Arguments", -> new CallNode($1, $2)
o "Value Arguments", -> new CallNode $1, $2
o "Invocation Arguments", -> new CallNode $1, $2
]
# The list of arguments to a function invocation.
# The list of arguments to a function call.
Arguments: [
o "CALL_START ArgList CALL_END", -> $2
o "CALL_START ArgList OptComma CALL_END", -> $2
]
# Calling super.
Super: [
o "SUPER CALL_START ArgList CALL_END", -> new CallNode('super', $3)
o "SUPER CALL_START ArgList OptComma CALL_END", -> new CallNode 'super', $3
]
# This references, either naked or to a property.
# A reference to the *this* current object.
This: [
o "@", -> new ValueNode(new LiteralNode('this'))
o "@ Identifier", -> new ValueNode(new LiteralNode('this'), [new AccessorNode($2)])
o "THIS", -> new ValueNode new LiteralNode 'this'
o "@", -> new ValueNode new LiteralNode 'this'
]
# The range literal.
# A reference to a property on *this*.
ThisProperty: [
o "@ Identifier", -> new ValueNode new LiteralNode('this'), [new AccessorNode($2)]
]
# The CoffeeScript range literal.
Range: [
o "[ Expression . . Expression ]", -> new RangeNode($2, $5)
o "[ Expression . . . Expression ]", -> new RangeNode($2, $6, true)
o "[ Expression . . Expression ]", -> new RangeNode $2, $5
o "[ Expression . . . Expression ]", -> new RangeNode $2, $6, true
]
# The slice literal.
Slice: [
o "INDEX_START Expression . . Expression INDEX_END", -> new RangeNode($2, $5)
o "INDEX_START Expression . . . Expression INDEX_END", -> new RangeNode($2, $6, true)
o "INDEX_START Expression . . Expression INDEX_END", -> new RangeNode $2, $5
o "INDEX_START Expression . . . Expression INDEX_END", -> new RangeNode $2, $6, true
]
# The array literal.
Array: [
o "[ ArgList ]", -> new ArrayNode($2)
o "[ ArgList OptComma ]", -> new ArrayNode $2
]
# A list of arguments to a method call, or as the contents of an array.
# The **ArgList** is both the list of objects passed into a function call,
# as well as the contents of an array literal
# (i.e. comma-separated expressions). Newlines work as well.
ArgList: [
o "", -> []
o "Expression", -> [$1]
@@ -343,128 +367,271 @@ grammar: {
o "ArgList TERMINATOR Expression", -> $1.concat [$3]
o "ArgList , TERMINATOR Expression", -> $1.concat [$4]
o "ArgList , INDENT Expression", -> $1.concat [$4]
o "ArgList OUTDENT", -> $1
o "ArgList OptComma OUTDENT"
]
# Just simple, comma-separated, required arguments (no fancy syntax).
# Just simple, comma-separated, required arguments (no fancy syntax). We need
# this to be separate from the **ArgList** for use in **Switch** blocks, where
# having the newlines wouldn't make sense.
SimpleArgs: [
o "Expression", -> $1
o "Expression"
o "SimpleArgs , Expression", ->
if $1 instanceof Array then $1.concat([$3]) else [$1].concat([$3])
]
# Try/catch/finally exception handling blocks.
# The variants of *try/catch/finally* exception handling blocks.
Try: [
o "TRY Block Catch", -> new TryNode($2, $3[0], $3[1])
o "TRY Block FINALLY Block", -> new TryNode($2, null, null, $4)
o "TRY Block Catch FINALLY Block", -> new TryNode($2, $3[0], $3[1], $5)
o "TRY Block Catch", -> new TryNode $2, $3[0], $3[1]
o "TRY Block FINALLY Block", -> new TryNode $2, null, null, $4
o "TRY Block Catch FINALLY Block", -> new TryNode $2, $3[0], $3[1], $5
]
# A catch clause.
# A catch clause names its error and runs a block of code.
Catch: [
o "CATCH Identifier Block", -> [$2, $3]
]
# Throw an exception.
# Throw an exception object.
Throw: [
o "THROW Expression", -> new ThrowNode($2)
o "THROW Expression", -> new ThrowNode $2
]
# Parenthetical expressions.
# Parenthetical expressions. Note that the **Parenthetical** is a **Value**,
# not an **Expression**, so if you need to use an expression in a place
# where only values are accepted, wrapping it in parentheses will always do
# the trick.
Parenthetical: [
o "( Expression )", -> new ParentheticalNode($2)
o "( Line )", -> new ParentheticalNode $2
]
# The condition for a while loop.
# A language extension to CoffeeScript from the outside. We simply pass
# it through unaltered.
Extension: [
o "EXTENSION"
]
# The condition portion of a while loop.
WhileSource: [
o "WHILE Expression", -> new WhileNode($2)
o "WHILE Expression WHEN Expression", -> new WhileNode($2, {filter : $4})
o "WHILE Expression", -> new WhileNode $2
o "WHILE Expression WHEN Expression", -> new WhileNode $2, {guard : $4}
o "UNTIL Expression", -> new WhileNode $2, {invert: true}
o "UNTIL Expression WHEN Expression", -> new WhileNode $2, {invert: true, guard: $4}
]
# The while loop. (there is no do..while).
# The while loop can either be normal, with a block of expressions to execute,
# or postfix, with a single expression. There is no do..while.
While: [
o "WhileSource Block", -> $1.add_body $2
o "Expression WhileSource", -> $2.add_body $1
o "Statement WhileSource", -> $2.add_body Expressions.wrap [$1]
o "Expression WhileSource", -> $2.add_body Expressions.wrap [$1]
]
# Array comprehensions, including guard and current index.
# Looks a little confusing, check nodes.rb for the arguments to ForNode.
# Array, object, and range comprehensions, at the most generic level.
# Comprehensions can either be normal, with a block of expressions to execute,
# or postfix, with a single expression.
For: [
o "Expression FOR ForVariables ForSource", -> new ForNode($1, $4, $3[0], $3[1])
o "FOR ForVariables ForSource Block", -> new ForNode($4, $3, $2[0], $2[1])
o "Statement FOR ForVariables ForSource", -> new ForNode $1, $4, $3[0], $3[1]
o "Expression FOR ForVariables ForSource", -> new ForNode $1, $4, $3[0], $3[1]
o "FOR ForVariables ForSource Block", -> new ForNode $4, $3, $2[0], $2[1]
]
# An array comprehension has variables for the current element and index.
# An array of all accepted values for a variable inside the loop. This
# enables support for pattern matching.
ForValue: [
o "Identifier"
o "Array", -> new ValueNode $1
o "Object", -> new ValueNode $1
]
# An array or range comprehension has variables for the current element and
# (optional) reference to the current index. Or, *key, value*, in the case
# of object comprehensions.
ForVariables: [
o "Identifier", -> [$1]
o "Identifier , Identifier", -> [$1, $3]
o "ForValue", -> [$1]
o "ForValue , ForValue", -> [$1, $3]
]
# The source of the array comprehension can optionally be filtered.
# The source of a comprehension is an array or object with an optional guard
# clause. If it's an array comprehension, you can also choose to step through
# in fixed-size increments.
ForSource: [
o "IN Expression", -> {source: $2}
o "OF Expression", -> {source: $2, object: true}
o "ForSource WHEN Expression", -> $1.filter: $3; $1
o "ForSource BY Expression", -> $1.step: $3; $1
o "IN Expression", -> {source: $2}
o "OF Expression", -> {source: $2, object: true}
o "IN Expression WHEN Expression", -> {source: $2, guard: $4}
o "OF Expression WHEN Expression", -> {source: $2, guard: $4, object: true}
o "IN Expression BY Expression", -> {source: $2, step: $4}
o "IN Expression WHEN Expression BY Expression", -> {source: $2, guard: $4, step: $6}
o "IN Expression BY Expression WHEN Expression", -> {source: $2, step: $4, guard: $6}
]
# Switch/When blocks.
# The CoffeeScript switch/when/else block replaces the JavaScript
# switch/case/default by compiling into an if-else chain.
Switch: [
o "SWITCH Expression INDENT Whens OUTDENT", -> $4.rewrite_condition($2)
o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.rewrite_condition($2).add_else($6, true)
o "SWITCH Expression INDENT Whens OUTDENT", -> $4.switches_over $2
o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.switches_over($2).add_else $6, true
o "SWITCH INDENT Whens OUTDENT", -> $3
o "SWITCH INDENT Whens ELSE Block OUTDENT", -> $3.add_else $5, true
]
# The inner list of whens.
# The inner list of whens is left recursive. At code-generation time, the
# IfNode will rewrite them into a proper chain.
Whens: [
o "When", -> $1
o "Whens When", -> $1.push $2
o "When"
o "Whens When", -> $1.add_else $2
]
# An individual when.
# An individual **When** clause, with action.
When: [
o "LEADING_WHEN SimpleArgs Block", -> new IfNode($2, $3, null, {statement: true})
o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode($2, $3, null, {statement: true})
o "Comment TERMINATOR When", -> $3.comment: $1; $3
o "LEADING_WHEN SimpleArgs Block", -> new IfNode $2, $3, {statement: true}
o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, {statement: true}
o "Comment TERMINATOR When", -> $3.comment: $1; $3
]
# The most basic form of "if".
# The most basic form of *if* is a condition and an action. The following
# if-related rules are broken up along these lines in order to avoid
# ambiguity.
IfStart: [
o "IF Expression Block", -> new IfNode($2, $3)
o "IfStart ElsIfs", -> $1.add_else($2)
o "IF Expression Block", -> new IfNode $2, $3
o "UNLESS Expression Block", -> new IfNode $2, $3, {invert: true}
o "IfStart ElsIf", -> $1.add_else $2
]
# An **IfStart** can optionally be followed by an else block.
IfBlock: [
o "IfStart", -> $1
o "IfStart ELSE Block", -> $1.add_else($3)
o "IfStart"
o "IfStart ELSE Block", -> $1.add_else $3
]
# Multiple elsifs can be chained together.
ElsIfs: [
# An *else if* continuation of the *if* expression.
ElsIf: [
o "ELSE IF Expression Block", -> (new IfNode($3, $4)).force_statement()
o "ElsIfs ElsIf", -> $1.add_else($2)
]
# The full complement of if blocks, including postfix one-liner ifs and unlesses.
# The full complement of *if* expressions, including postfix one-liner
# *if* and *unless*.
If: [
o "IfBlock", -> $1
o "Expression IF Expression", -> new IfNode($3, Expressions.wrap([$1]), null, {statement: true})
o "Expression UNLESS Expression", -> new IfNode($3, Expressions.wrap([$1]), null, {statement: true, invert: true})
o "IfBlock"
o "Statement IF Expression", -> new IfNode $3, Expressions.wrap([$1]), {statement: true}
o "Expression IF Expression", -> new IfNode $3, Expressions.wrap([$1]), {statement: true}
o "Statement UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), {statement: true, invert: true}
o "Expression UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), {statement: true, invert: true}
]
# Arithmetic and logical operators, working on one or more operands.
# Here they are grouped by order of precedence. The actual precedence rules
# are defined at the bottom of the page. It would be shorter if we could
# combine most of these rules into a single generic *Operand OpSymbol Operand*
# -type rule, but in order to make the precedence binding possible, separate
# rules are necessary.
Operation: [
o "! Expression", -> new OpNode '!', $2
o "!! Expression", -> new OpNode '!!', $2
o("- Expression", (-> new OpNode('-', $2)), {prec: 'UMINUS'})
o("+ Expression", (-> new OpNode('+', $2)), {prec: 'UPLUS'})
o "~ Expression", -> new OpNode '~', $2
o "-- Expression", -> new OpNode '--', $2
o "++ Expression", -> new OpNode '++', $2
o "DELETE Expression", -> new OpNode 'delete', $2
o "TYPEOF Expression", -> new OpNode 'typeof', $2
o "Expression --", -> new OpNode '--', $1, null, true
o "Expression ++", -> new OpNode '++', $1, null, true
o "Expression * Expression", -> new OpNode '*', $1, $3
o "Expression / Expression", -> new OpNode '/', $1, $3
o "Expression % Expression", -> new OpNode '%', $1, $3
o "Expression + Expression", -> new OpNode '+', $1, $3
o "Expression - Expression", -> new OpNode '-', $1, $3
o "Expression << Expression", -> new OpNode '<<', $1, $3
o "Expression >> Expression", -> new OpNode '>>', $1, $3
o "Expression >>> Expression", -> new OpNode '>>>', $1, $3
o "Expression & Expression", -> new OpNode '&', $1, $3
o "Expression | Expression", -> new OpNode '|', $1, $3
o "Expression ^ Expression", -> new OpNode '^', $1, $3
o "Expression <= Expression", -> new OpNode '<=', $1, $3
o "Expression < Expression", -> new OpNode '<', $1, $3
o "Expression > Expression", -> new OpNode '>', $1, $3
o "Expression >= Expression", -> new OpNode '>=', $1, $3
o "Expression == Expression", -> new OpNode '==', $1, $3
o "Expression != Expression", -> new OpNode '!=', $1, $3
o "Expression && Expression", -> new OpNode '&&', $1, $3
o "Expression || Expression", -> new OpNode '||', $1, $3
o "Expression ? Expression", -> new OpNode '?', $1, $3
o "Expression -= Expression", -> new OpNode '-=', $1, $3
o "Expression += Expression", -> new OpNode '+=', $1, $3
o "Expression /= Expression", -> new OpNode '/=', $1, $3
o "Expression *= Expression", -> new OpNode '*=', $1, $3
o "Expression %= Expression", -> new OpNode '%=', $1, $3
o "Expression ||= Expression", -> new OpNode '||=', $1, $3
o "Expression &&= Expression", -> new OpNode '&&=', $1, $3
o "Expression ?= Expression", -> new OpNode '?=', $1, $3
o "Expression INSTANCEOF Expression", -> new OpNode 'instanceof', $1, $3
o "Expression IN Expression", -> new OpNode 'in', $1, $3
]
}
# Helpers ==============================================================
# Precedence
# ----------
# Make the Jison parser.
bnf: {}
# Operators at the top of this list have higher precedence than the ones lower
# down. Following these rules is what makes `2 + 3 * 4` parse as:
#
# 2 + (3 * 4)
#
# And not:
#
# (2 + 3) * 4
operators: [
["left", '?']
["nonassoc", 'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--']
["left", '*', '/', '%']
["left", '+', '-']
["left", '<<', '>>', '>>>']
["left", '&', '|', '^']
["left", '<=', '<', '>', '>=']
["right", 'DELETE', 'INSTANCEOF', 'TYPEOF']
["left", '==', '!=']
["left", '&&', '||']
["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=']
["left", '.']
["right", 'INDENT']
["left", 'OUTDENT']
["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW']
["right", 'FOR', 'WHILE', 'UNTIL', 'NEW', 'SUPER', 'CLASS']
["left", 'EXTENDS']
["right", 'ASSIGN', 'RETURN']
["right", '->', '=>', '<-', 'UNLESS', 'IF', 'ELSE']
]
# Wrapping Up
# -----------
# Finally, now what we have our **grammar** and our **operators**, we can create
# our **Jison.Parser**. We do this by processing all of our rules, recording all
# terminals (every symbol which does not appear as the name of a rule above)
# as "tokens".
tokens: []
for name, non_terminal of grammar
bnf[name]: for option in non_terminal
for part in option[0].split(" ")
if !grammar[part]
tokens.push(part)
if name == "Root"
option[1] = "return " + option[1]
option
tokens: tokens.join(" ")
exports.parser: new Parser({tokens: tokens, bnf: bnf, operators: operators.reverse(), startSymbol: 'Root'}, {debug: false})
for name, alternatives of grammar
grammar[name]: for alt in alternatives
for token in alt[0].split ' '
tokens.push token unless grammar[token]
alt[1] = "return ${alt[1]}" if name is 'Root'
alt
# Initialize the **Parser** with our list of terminal **tokens**, our **grammar**
# rules, and the name of the root. Reverse the operators because Jison orders
# precedence from low to high, and we have it high to low
# (as in [Yacc](http://dinosaur.compilertools.net/yacc/index.html)).
exports.parser: new Parser {
tokens: tokens.join ' '
bnf: grammar
operators: operators.reverse()
startSymbol: 'Root'
}

95
src/helpers.coffee Normal file
View File

@@ -0,0 +1,95 @@
# This file contains the common helper functions that we'd like to share among
# the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten
# arrays, count characters, that sort of thing.
# Set up exported variables for both **Node.js** and the browser.
this.exports: this unless process?
helpers: exports.helpers: {}
# Cross-browser indexOf, so that IE can join the party.
helpers.index_of: index_of: (array, item, from) ->
return array.indexOf item, from if array.indexOf
for other, index in array
if other is item and (not from or (from <= index))
return index
-1
# Does a list include a value?
helpers.include: include: (list, value) ->
index_of(list, value) >= 0
# Peek at the beginning of a given string to see if it matches a sequence.
helpers.starts: starts: (string, literal, start) ->
string.substring(start, (start or 0) + literal.length) is literal
# Trim out all falsy values from an array.
helpers.compact: compact: (array) -> item for item in array when item
# Count the number of occurences of a character in a string.
helpers.count: count: (string, letter) ->
num: 0
pos: index_of string, letter
while pos isnt -1
num: + 1
pos: index_of string, letter, pos + 1
num
# Merge objects, returning a fresh copy with attributes from both sides.
# Used every time `BaseNode#compile` is called, to allow properties in the
# options hash to propagate down the tree without polluting other branches.
helpers.merge: merge: (options, overrides) ->
fresh: {}
(fresh[key]: val) for key, val of options
(fresh[key]: val) for key, val of overrides if overrides
fresh
# Extend a source object with the properties of another object (shallow copy).
# We use this to simulate Node's deprecated `process.mixin`
helpers.extend: extend: (object, properties) ->
(object[key]: val) for key, val of properties
# Return a completely flattened version of an array. Handy for getting a
# list of `children` from the nodes.
helpers.flatten: flatten: (array) ->
memo: []
for item in array
if item instanceof Array then memo: memo.concat(item) else memo.push(item)
memo
# Delete a key from an object, returning the value. Useful when a node is
# looking for a particular method in an options hash.
helpers.del: del: (obj, key) ->
val: obj[key]
delete obj[key]
val
# Matches a balanced group such as a single or double-quoted string. Pass in
# a series of delimiters, all of which must be nested correctly within the
# contents of the string. This method allows us to have strings within
# interpolations within strings, ad infinitum.
helpers.balanced_string: balanced_string: (str, delimited, options) ->
options: or {}
slash: delimited[0][0] is '/'
levels: []
i: 0
while i < str.length
if levels.length and starts str, '\\', i
i: + 1
else
for pair in delimited
[open, close]: pair
if levels.length and starts(str, close, i) and levels[levels.length - 1] is pair
levels.pop()
i: + close.length - 1
i: + 1 unless levels.length
break
else if starts str, open, i
levels.push(pair)
i: + open.length - 1
break
break if not levels.length or slash and starts str, '\n', i
i: + 1
if levels.length
return false if slash
throw new Error "SyntaxError: Unterminated ${levels.pop()[0]} starting on line ${@line + 1}"
if not i then false else str.substring(0, i)

2
src/index.coffee Normal file
View File

@@ -0,0 +1,2 @@
# Loader for CoffeeScript as a Node.js library.
(exports[key]: val) for key, val of require './coffee-script'

View File

@@ -9,217 +9,203 @@
# Set up the Lexer for both Node.js and the browser, depending on where we are.
if process?
Rewriter: require('./rewriter').Rewriter
{Rewriter}: require('./rewriter')
{helpers}: require('./helpers')
else
this.exports: this
Rewriter: this.Rewriter
Rewriter: this.Rewriter
helpers: this.helpers
# Constants
# ---------
# Keywords that CoffeeScript shares in common with JavaScript.
JS_KEYWORDS: [
"if", "else",
"true", "false",
"new", "return",
"try", "catch", "finally", "throw",
"break", "continue",
"for", "in", "while",
"delete", "instanceof", "typeof",
"switch", "super", "extends", "class"
]
# CoffeeScript-only keywords, which we're more relaxed about allowing. They can't
# be used standalone, but you can reference them as an attached property.
COFFEE_KEYWORDS: [
"then", "unless",
"yes", "no", "on", "off",
"and", "or", "is", "isnt", "not",
"of", "by", "where", "when"
]
# The combined list of keywords is the superset that gets passed verbatim to
# the parser.
KEYWORDS: JS_KEYWORDS.concat COFFEE_KEYWORDS
# The list of keywords that are reserved by JavaScript, but not used, or are
# used by CoffeeScript internally. We throw an error when these are encountered,
# to avoid having a JavaScript error at runtime.
RESERVED: [
"case", "default", "do", "function", "var", "void", "with"
"const", "let", "debugger", "enum", "export", "import", "native",
"__extends", "__hasProp"
]
# The superset of both JavaScript keywords and reserved words, none of which may
# be used as identifiers or properties.
JS_FORBIDDEN: JS_KEYWORDS.concat RESERVED
# Token matching regexes.
IDENTIFIER : /^([a-zA-Z$_](\w|\$)*)/
NUMBER : /^(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
STRING : /^(""|''|"([\s\S]*?)([^\\]|\\\\)"|'([\s\S]*?)([^\\]|\\\\)')/
HEREDOC : /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/
JS : /^(``|`([\s\S]*?)([^\\]|\\\\)`)/
OPERATOR : /^([+\*&|\/\-%=<>:!?]+)/
WHITESPACE : /^([ \t]+)/
COMMENT : /^(((\n?[ \t]*)?#[^\n]*)+)/
CODE : /^((-|=)>)/
REGEX : /^(\/(\S.*?)?([^\\]|\\\\)\/[imgy]{0,4})/
MULTI_DENT : /^((\n([ \t]*))+)(\.)?/
LAST_DENTS : /\n([ \t]*)/g
LAST_DENT : /\n([ \t]*)/
ASSIGNMENT : /^(:|=)$/
# Token cleaning regexes.
JS_CLEANER : /(^`|`$)/g
MULTILINER : /\n/g
STRING_NEWLINES : /\n[ \t]*/g
COMMENT_CLEANER : /(^[ \t]*#|\n[ \t]*$)/mg
NO_NEWLINE : /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/
HEREDOC_INDENT : /^[ \t]+/mg
# Tokens which a regular expression will never immediately follow, but which
# a division operator might.
#
# See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
#
# Our list is shorter, due to sans-parentheses method calls.
NOT_REGEX: [
'NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE'
]
# Tokens which could legitimately be invoked or indexed. A opening
# parentheses or bracket following these tokens will be recorded as the start
# of a function invocation or indexing operation.
CALLABLE: ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@']
# Tokens that indicate an access -- keywords immediately following will be
# treated as identifiers.
ACCESSORS: ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@']
# Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
# occurs at the start of a line. We disambiguate these from trailing whens to
# avoid an ambiguity in the grammar.
BEFORE_WHEN: ['INDENT', 'OUTDENT', 'TERMINATOR']
# Import the helpers we need.
{include, count, starts, compact, balanced_string}: helpers
# The Lexer Class
# ---------------
# The Lexer class reads a stream of CoffeeScript and divvys it up into tagged
# tokens. A minor bit of the ambiguity in the grammar has been avoided by
# tokens. Some potential ambiguity in the grammar has been avoided by
# pushing some extra smarts into the Lexer.
exports.Lexer: class Lexer
# Scan by attempting to match tokens one at a time. Slow and steady.
tokenize: (code) ->
@code : code # The remainder of the source code.
@i : 0 # Current character position we're parsing.
@line : 0 # The current line.
@indent : 0 # The current indent level.
@indents : [] # The stack of all indent levels we are currently within.
@tokens : [] # Collection of all parsed tokens in the form ['TOKEN_TYPE', value, line]
# **tokenize** is the Lexer's main method. Scan by attempting to match tokens
# one at a time, using a regular expression anchored at the start of the
# remaining code, or a custom recursive token-matching method
# (for interpolations). When the next token has been recorded, we move forward
# within the code past the token, and begin again.
#
# Each tokenizing method is responsible for incrementing `@i` by the number of
# characters it has consumed. `@i` can be thought of as our finger on the page
# of source.
#
# Before returning the token stream, run it through the [Rewriter](rewriter.html)
# unless explicitly asked not to.
tokenize: (code, options) ->
code : code.replace /(\r|\s+$)/g, ''
o : options or {}
@code : code # The remainder of the source code.
@i : 0 # Current character position we're parsing.
@line : o.line or 0 # The current line.
@indent : 0 # The current indentation level.
@indents : [] # The stack of all current indentation levels.
@tokens : [] # Stream of parsed tokens in the form ['TYPE', value, line]
while @i < @code.length
@chunk: @code.slice(@i)
@chunk: @code.slice @i
@extract_next_token()
@close_indentation()
return @tokens if o.rewrite is off
(new Rewriter()).rewrite @tokens
# At every position, run through this list of attempted matches,
# short-circuiting if any of them succeed.
# short-circuiting if any of them succeed. Their order determines precedence:
# `@literal_token` is the fallback catch-all.
extract_next_token: ->
return if @extension_token()
return if @identifier_token()
return if @number_token()
return if @heredoc_token()
return if @string_token()
return if @js_token()
return if @regex_token()
return if @comment_token()
return if @line_token()
return if @whitespace_token()
return if @js_token()
return if @string_token()
return @literal_token()
# Tokenizers
# ----------
# Language extensions get the highest priority, first chance to tag tokens
# as something else.
extension_token: ->
for extension in Lexer.extensions
return true if extension.call this
false
# Matches identifying literals: variables, keywords, method names, etc.
# Check to ensure that JavaScript reserved words aren't being used as
# identifiers. Because CoffeeScript reserves a handful of keywords that are
# allowed in JavaScript, we're careful not to tag them as keywords when
# referenced as property names here, so you can still do `jQuery.is()` even
# though `is` means `===` otherwise.
identifier_token: ->
return false unless id: @match IDENTIFIER, 1
@name_access_type()
accessed: include ACCESSORS, @tag 0
tag: 'IDENTIFIER'
tag: id.toUpperCase() if include(KEYWORDS, id) and
not (include(ACCESSORS, @tag(0)) and not @prev().spaced)
@identifier_error id if include RESERVED, id
tag: 'LEADING_WHEN' if tag is 'WHEN' and include BEFORE_WHEN, @tag()
@token(tag, id)
@i += id.length
tag: id.toUpperCase() if not accessed and include(KEYWORDS, id)
@identifier_error id if include RESERVED, id
tag: 'LEADING_WHEN' if tag is 'WHEN' and include LINE_BREAK, @tag()
@i: + id.length
unless accessed
tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id
return @tag_half_assignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
@token tag, id
true
# Matches numbers, including decimals, hex, and exponential notation.
number_token: ->
return false unless number: @match NUMBER, 1
@token 'NUMBER', number
@i += number.length
@i: + number.length
true
# Matches strings, including multi-line strings.
# Matches strings, including multi-line strings. Ensures that quotation marks
# are balanced within the string's contents, and within nested interpolations.
string_token: ->
return false unless string: @match STRING, 1
escaped: string.replace STRING_NEWLINES, " \\\n"
@token 'STRING', escaped
@line += count string, "\n"
@i += string.length
return false unless starts(@chunk, '"') or starts(@chunk, "'")
return false unless string:
@balanced_token(['"', '"'], ['${', '}']) or
@balanced_token ["'", "'"]
@interpolate_string string.replace STRING_NEWLINES, " \\\n"
@line: + count string, "\n"
@i: + string.length
true
# Matches heredocs, adjusting indentation to the correct level.
# Matches heredocs, adjusting indentation to the correct level, as heredocs
# preserve whitespace, but ignore indentation to the left.
heredoc_token: ->
return false unless match = @chunk.match(HEREDOC)
doc: @sanitize_heredoc match[2] or match[4]
@token 'STRING', '"' + doc + '"'
@line += count match[1], "\n"
@i += match[1].length
return false unless match: @chunk.match(HEREDOC)
quote: match[1].substr 0, 1
doc: @sanitize_heredoc match[2] or match[4], {quote}
@interpolate_string "$quote$doc$quote"
@line: + count match[1], "\n"
@i: + match[1].length
true
# Matches interpolated JavaScript.
js_token: ->
return false unless script: @match JS, 1
@token 'JS', script.replace(JS_CLEANER, '')
@i += script.length
true
# Matches regular expression literals.
regex_token: ->
return false unless regex: @match REGEX, 1
return false if include NOT_REGEX, @tag()
@token 'REGEX', regex
@i += regex.length
true
# Matches and conumes comments.
# Matches and conumes comments. We pass through comments into JavaScript,
# so they're treated as real tokens, like any other part of the language.
comment_token: ->
return false unless comment: @match COMMENT, 1
@line += (comment.match(MULTILINER) or []).length
lines: comment.replace(COMMENT_CLEANER, '').split(MULTILINER)
@token 'COMMENT', compact lines
@token 'TERMINATOR', "\n"
@i += comment.length
return false unless match: @chunk.match(COMMENT)
if match[3]
comment: @sanitize_heredoc match[3], {herecomment: true}
@token 'HERECOMMENT', comment.split MULTILINER
else
lines: compact match[1].replace(COMMENT_CLEANER, '').split MULTILINER
i: @tokens.length - 1
if @unfinished()
i: - 1 while @tokens[i] and not include LINE_BREAK, @tokens[i][0]
@tokens.splice(i + 1, 0, ['COMMENT', lines, @line], ['TERMINATOR', '\n', @line])
@line: + count match[1], "\n"
@i: + match[1].length
true
# Matches JavaScript interpolated directly into the source via backticks.
js_token: ->
return false unless starts @chunk, '`'
return false unless script: @balanced_token ['`', '`']
@token 'JS', script.replace JS_CLEANER, ''
@i: + script.length
true
# Matches regular expression literals. Lexing regular expressions is difficult
# to distinguish from division, so we borrow some basic heuristics from
# JavaScript and Ruby, borrow slash balancing from `@balanced_token`, and
# borrow interpolation from `@interpolate_string`.
regex_token: ->
return false unless @chunk.match REGEX_START
return false if include NOT_REGEX, @tag()
return false unless regex: @balanced_token ['/', '/']
return false unless end: @chunk.substr(regex.length).match REGEX_END
regex: + flags: end[2] if end[2]
if regex.match REGEX_INTERPOLATION
str: regex.substring(1).split('/')[0]
str: str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped
@tokens: @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]
@interpolate_string "\"$str\"", yes
@tokens: @tokens.concat [[',', ','], ['STRING', "\"$flags\""], [')', ')'], [')', ')']]
else
@token 'REGEX', regex
@i: + regex.length
true
# Matches a token in which which the passed delimiter pairs must be correctly
# balanced (ie. strings, JS literals).
balanced_token: (delimited...) ->
balanced_string @chunk, delimited
# Matches newlines, indents, and outdents, and determines which is which.
# If we can detect that the current line is continued onto the the next line,
# then the newline is suppressed:
#
# elements
# .each( ... )
# .map( ... )
#
# Keeps track of the level of indentation, because a single outdent token
# can close multiple indents, so we need to know how far in we happen to be.
line_token: ->
return false unless indent: @match MULTI_DENT, 1
@line += indent.match(MULTILINER).length
@i += indent.length
@line: + count indent, "\n"
@i : + indent.length
prev: @prev(2)
size: indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length
next_character: @chunk.match(MULTI_DENT)[4]
no_newlines: next_character is '.' or (@value() and @value().match(NO_NEWLINE) and
prev and (prev[0] isnt '.') and not @value().match(CODE))
no_newlines: next_character is '.' or @unfinished()
if size is @indent
return @suppress_newlines(indent) if no_newlines
return @newline_token(indent)
return @suppress_newlines() if no_newlines
return @newline_token indent
else if size > @indent
return @suppress_newlines(indent) if no_newlines
return @suppress_newlines() if no_newlines
diff: size - @indent
@token 'INDENT', diff
@indents.push diff
@@ -228,13 +214,13 @@ exports.Lexer: class Lexer
@indent: size
true
# Record an outdent token or tokens, if we happen to be moving back inwards
# past multiple recorded indents.
# Record an outdent token or multiple tokens, if we happen to be moving back
# inwards past several recorded indents.
outdent_token: (move_out, no_newlines) ->
while move_out > 0 and @indents.length
last_indent: @indents.pop()
@token 'OUTDENT', last_indent
move_out -= last_indent
move_out: - last_indent
@token 'TERMINATOR', "\n" unless @tag() is 'TERMINATOR' or no_newlines
true
@@ -244,47 +230,51 @@ exports.Lexer: class Lexer
return false unless space: @match WHITESPACE, 1
prev: @prev()
prev.spaced: true if prev
@i += space.length
@i: + space.length
true
# Generate a newline token. Multiple newlines get merged together.
# Generate a newline token. Consecutive newlines get merged together.
newline_token: (newlines) ->
@token 'TERMINATOR', "\n" unless @tag() is 'TERMINATOR'
true
# Use a `\` at a line-ending to suppress the newline.
# The slash is removed here once its job is done.
suppress_newlines: (newlines) ->
suppress_newlines: ->
@tokens.pop() if @value() is "\\"
true
# We treat all other single characters as a token. Eg.: `( ) , . !`
# Multi-character operators are also literal tokens, so that Jison can assign
# the proper order of operations.
# the proper order of operations. There are some symbols that we tag specially
# here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish
# parentheses that indicate a method call from regular parentheses, and so on.
literal_token: ->
match: @chunk.match(OPERATOR)
match: @chunk.match OPERATOR
value: match and match[1]
@tag_parameters() if value and value.match(CODE)
value ||= @chunk.substr(0, 1)
not_spaced: not @prev() or not @prev().spaced
space: match and match[2]
@tag_parameters() if value and value.match CODE
value: or @chunk.substr 0, 1
prev_spaced: @prev() and @prev().spaced
tag: value
if value.match(ASSIGNMENT)
if value.match ASSIGNMENT
tag: 'ASSIGN'
@assignment_error() if include JS_FORBIDDEN, @value
else if value is ';'
tag: 'TERMINATOR'
else if value is '[' and @tag() is '?' and not_spaced
else if value is '[' and @tag() is '?' and not prev_spaced
tag: 'SOAKED_INDEX_START'
@soaked_index: true
@tokens.pop()
else if value is ']' and @soaked_index
tag: 'SOAKED_INDEX_END'
@soaked_index: false
else if include(CALLABLE, @tag()) and not_spaced
else if include(CALLABLE, @tag()) and not prev_spaced
tag: 'CALL_START' if value is '('
tag: 'INDEX_START' if value is '['
@i: + value.length
return @tag_half_assignment tag if space and prev_spaced and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
@token tag, value
@i += value.length
true
# Token Manipulators
@@ -301,61 +291,127 @@ exports.Lexer: class Lexer
else
@tag 1, 'PROPERTY_ACCESS'
# Sanitize a heredoc by escaping double quotes and erasing all external
# indentation on the left-hand side.
sanitize_heredoc: (doc) ->
indent: (doc.match(HEREDOC_INDENT) or ['']).sort()[0]
doc.replace(new RegExp("^" + indent, 'gm'), '')
.replace(MULTILINER, "\\n")
.replace(/"/g, '\\"')
# Sanitize a heredoc or herecomment by escaping internal double quotes and
# erasing all external indentation on the left-hand side.
sanitize_heredoc: (doc, options) ->
while match: HEREDOC_INDENT.exec doc
attempt: if match[2]? then match[2] else match[3]
indent: attempt if not indent or attempt.length < indent.length
doc: doc.replace(new RegExp("^" +indent, 'gm'), '')
return doc if options.herecomment
doc.replace(MULTILINER, "\\n")
.replace(new RegExp(options.quote, 'g'), '\\"')
# A source of ambiguity in our grammar was parameter lists in function
# definitions (as opposed to argument lists in function calls). Tag
# parameter identifiers in order to avoid this. Also, parameter lists can
# make use of splats.
# Tag a half assignment.
tag_half_assignment: (tag) ->
last: @tokens.pop()
@tokens.push ["$tag=", "$tag=", last[2]]
true
# A source of ambiguity in our grammar used to be parameter lists in function
# definitions versus argument lists in function calls. Walk backwards, tagging
# parameters specially in order to make things easier for the parser.
tag_parameters: ->
return if @tag() isnt ')'
i: 0
while true
i += 1
tok: @prev(i)
i: + 1
tok: @prev i
return if not tok
switch tok[0]
when 'IDENTIFIER' then tok[0]: 'PARAM'
when ')' then tok[0]: 'PARAM_END'
when '(' then return tok[0]: 'PARAM_START'
when 'IDENTIFIER' then tok[0]: 'PARAM'
when ')' then tok[0]: 'PARAM_END'
when '(', 'CALL_START' then return tok[0]: 'PARAM_START'
true
# Close up all remaining open blocks at the end of the file.
close_indentation: ->
@outdent_token(@indent)
@outdent_token @indent
# Error for when you try to use a forbidden word in JavaScript as
# The error for when you try to use a forbidden word in JavaScript as
# an identifier.
identifier_error: (word) ->
throw new Error 'SyntaxError: Reserved word "' + word + '" on line ' + @line
throw new Error "SyntaxError: Reserved word \"$word\" on line ${@line + 1}"
# Error for when you try to assign to a reserved word in JavaScript,
# The error for when you try to assign to a reserved word in JavaScript,
# like "function" or "default".
assignment_error: ->
throw new Error 'SyntaxError: Reserved word "' + @value() + '" on line ' + @line + ' can\'t be assigned'
throw new Error "SyntaxError: Reserved word \"${@value()}\" on line ${@line + 1} can't be assigned"
# Expand variables and expressions inside double-quoted strings using
# [ECMA Harmony's interpolation syntax](http://wiki.ecmascript.org/doku.php?id=strawman:string_interpolation)
# for substitution of bare variables as well as arbitrary expressions.
#
# "Hello $name."
# "Hello ${name.capitalize()}."
#
# If it encounters an interpolation, this method will recursively create a
# new Lexer, tokenize the interpolated contents, and merge them into the
# token stream.
interpolate_string: (str, escape_quotes) ->
if str.length < 3 or not starts str, '"'
@token 'STRING', str
else
lexer: new Lexer()
tokens: []
quote: str.substring 0, 1
[i, pi]: [1, 1]
while i < str.length - 1
if starts str, '\\', i
i: + 1
else if match: str.substring(i).match INTERPOLATION
[group, interp]: match
interp: "this.${ interp.substring(1) }" if starts interp, '@'
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
tokens.push ['IDENTIFIER', interp]
i: + group.length - 1
pi: i + 1
else if (expr: balanced_string str.substring(i), [['${', '}']])
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
inner: expr.substring(2, expr.length - 1)
if inner.length
nested: lexer.tokenize "($inner)", {line: @line}
(tok[0]: ')') for tok, idx in nested when tok[0] is 'CALL_END'
nested.pop()
tokens.push ['TOKENS', nested]
else
tokens.push ['STRING', "$quote$quote"]
i: + expr.length - 1
pi: i + 1
i: + 1
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i and pi < str.length - 1
tokens.unshift ['STRING', '""'] unless tokens[0][0] is 'STRING'
interpolated: tokens.length > 1
@token '(', '(' if interpolated
for token, i in tokens
[tag, value]: token
if tag is 'TOKENS'
@tokens: @tokens.concat value
else if tag is 'STRING' and escape_quotes
escaped: value.substring(1, value.length - 1).replace(/"/g, '\\"')
@token tag, "\"$escaped\""
else
@token tag, value
@token '+', '+' if i < tokens.length - 1
@token ')', ')' if interpolated
tokens
# Helpers
# -------
# Add a token to the results, taking note of the line number.
token: (tag, value) ->
@tokens.push([tag, value, @line])
@tokens.push [tag, value, @line]
# Peek at a tag in the current token stream.
tag: (index, tag) ->
return unless tok: @prev(index)
return tok[0]: tag if tag?
tag: (index, new_tag) ->
return unless tok: @prev index
return tok[0]: new_tag if new_tag?
tok[0]
# Peek at a value in the current token stream.
value: (index, val) ->
return unless tok: @prev(index)
return unless tok: @prev index
return tok[1]: val if val?
tok[1]
@@ -366,24 +422,122 @@ exports.Lexer: class Lexer
# Attempt to match a string against the current chunk, returning the indexed
# match if successful, and `false` otherwise.
match: (regex, index) ->
return false unless m: @chunk.match(regex)
return false unless m: @chunk.match regex
if m then m[index] else false
# Utility Functions
# -----------------
# Are we in the midst of an unfinished expression?
unfinished: ->
prev: @prev(2)
@value() and @value().match and @value().match(NO_NEWLINE) and
prev and (prev[0] isnt '.') and not @value().match(CODE)
# Does a list include a value?
include: (list, value) ->
list.indexOf(value) >= 0
# Lexer Properties
# ----------------
# Trim out all falsy values from an array.
compact: (array) -> item for item in array when item
# There are no exensions to the core lexer by default.
@extensions: []
# Count the number of occurences of a character in a string.
count: (string, letter) ->
num: 0
pos: string.indexOf(letter)
while pos isnt -1
num += 1
pos: string.indexOf(letter, pos + 1)
num
# Constants
# ---------
# Keywords that CoffeeScript shares in common with JavaScript.
JS_KEYWORDS: [
"if", "else",
"true", "false",
"new", "return",
"try", "catch", "finally", "throw",
"break", "continue",
"for", "in", "while",
"delete", "instanceof", "typeof",
"switch", "super", "extends", "class",
"this", "null"
]
# CoffeeScript-only keywords, which we're more relaxed about allowing. They can't
# be used standalone, but you can reference them as an attached property.
COFFEE_ALIASES: ["and", "or", "is", "isnt", "not"]
COFFEE_KEYWORDS: COFFEE_ALIASES.concat [
"then", "unless", "until",
"yes", "no", "on", "off",
"of", "by", "where", "when"
]
# The combined list of keywords is the superset that gets passed verbatim to
# the parser.
KEYWORDS: JS_KEYWORDS.concat COFFEE_KEYWORDS
# The list of keywords that are reserved by JavaScript, but not used, or are
# used by CoffeeScript internally. We throw an error when these are encountered,
# to avoid having a JavaScript error at runtime.
RESERVED: [
"case", "default", "do", "function", "var", "void", "with"
"const", "let", "enum", "export", "import", "native"
]
# The superset of both JavaScript keywords and reserved words, none of which may
# be used as identifiers or properties.
JS_FORBIDDEN: JS_KEYWORDS.concat RESERVED
# Token matching regexes.
IDENTIFIER : /^([a-zA-Z\$_](\w|\$)*)/
NUMBER : /^(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
HEREDOC : /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/
INTERPOLATION : /^\$([a-zA-Z_@]\w*(\.\w+)*)/
OPERATOR : /^([+\*&|\/\-%=<>:!?]+)([ \t]*)/
WHITESPACE : /^([ \t]+)/
COMMENT : /^((\n?[ \t]*)?#{3}(?!#)\n*([\s\S]*?)\n*([ \t]*)#{3}|((\n?[ \t]*)?#[^\n]*)+)/
CODE : /^((-|=)>)/
MULTI_DENT : /^((\n([ \t]*))+)(\.)?/
LAST_DENTS : /\n([ \t]*)/g
LAST_DENT : /\n([ \t]*)/
ASSIGNMENT : /^(:|=)$/
# Regex-matching-regexes.
REGEX_START : /^\/[^\/ ]/
REGEX_INTERPOLATION: /([^\\]\$[a-zA-Z_@]|[^\\]\$\{.*[^\\]\})/
REGEX_END : /^(([imgy]{1,4})\b|\W)/
REGEX_ESCAPE : /\\[^\$]/g
# Token cleaning regexes.
JS_CLEANER : /(^`|`$)/g
MULTILINER : /\n/g
STRING_NEWLINES : /\n[ \t]*/g
COMMENT_CLEANER : /(^[ \t]*#|\n[ \t]*$)/mg
NO_NEWLINE : /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/
HEREDOC_INDENT : /(\n+([ \t]*)|^([ \t]+))/g
# Tokens which a regular expression will never immediately follow, but which
# a division operator might.
#
# See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
#
# Our list is shorter, due to sans-parentheses method calls.
NOT_REGEX: [
'NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']'
]
# Tokens which could legitimately be invoked or indexed. A opening
# parentheses or bracket following these tokens will be recorded as the start
# of a function invocation or indexing operation.
CALLABLE: ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS']
# Tokens that indicate an access -- keywords immediately following will be
# treated as identifiers.
ACCESSORS: ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@']
# Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
# 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']
# Half-assignments...
HALF_ASSIGNMENTS: ['-', '+', '/', '*', '%', '||', '&&', '?']
# Conversions from CoffeeScript operators into JavaScript ones.
CONVERSIONS: {
'and': '&&'
'or': '||'
'is': '=='
'isnt': '!='
'not': '!'
}

View File

@@ -1,42 +0,0 @@
# The Narwhal-compatibility wrapper for CoffeeScript.
# Require external dependencies.
os: require 'os'
file: require 'file'
coffee: require './coffee-script'
# Alias print to "puts", for Node.js compatibility:
puts: print
# Compile a string of CoffeeScript into JavaScript.
exports.compile: (source) ->
coffee.compile source
# Compile a given CoffeeScript file into JavaScript.
exports.compileFile: (path) ->
coffee.compile file.read path
# Make a factory for the CoffeeScript environment.
exports.makeNarwhalFactory: (path) ->
code: exports.compileFile path
factoryText: "function(require,exports,module,system,print){" + code + "/**/\n}"
if system.engine is "rhino"
Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null)
else
# eval requires parentheses, but parentheses break compileFunction.
eval "(" + factoryText + ")"
# The Narwhal loader for '.coffee' files.
factories: {}
loader: {}
# Reload the coffee-script environment from source.
loader.reload: (topId, path) ->
factories[topId]: ->
exports.makeNarwhalFactory path
# Ensure that the coffee-script environment is loaded.
loader.load: (topId, path) ->
factories[topId] ||= this.reload topId, path
require.loader.loaders.unshift [".coffee", loader]

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,53 @@
# Create an OptionParser with a list of valid options, in the form:
# [short-flag (optional), long-flag, description]
# And an optional banner for the usage help.
# A simple **OptionParser** class to parse option flags from the command-line.
# Use it like so:
#
# parser: new OptionParser switches, help_banner
# options: parser.parse process.argv
exports.OptionParser: class OptionParser
# Initialize with a list of valid options, in the form:
#
# [short-flag, long-flag, description]
#
# Along with an an optional banner for the usage help.
constructor: (rules, banner) ->
@banner: banner
@rules: build_rules(rules)
# Parse the argument array, populating an options object with all of the
# specified options, and returning it. options.arguments will be an array
# containing the remaning non-option arguments.
# Parse the list of arguments, populating an `options` object with all of the
# specified options, and returning it. `options.arguments` will be an array
# containing the remaning non-option arguments. This is a simpler API than
# many option parsers that allow you to attach callback actions for every
# flag. Instead, you're responsible for interpreting the options object.
parse: (args) ->
options: {arguments: []}
args: normalize_arguments args
while arg: args.shift()
while (arg: args.shift())
is_option: !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
matched_rule: no
for rule in @rules
if rule.letter is arg or rule.flag is arg
if rule.short_flag is arg or rule.long_flag is arg
options[rule.name]: if rule.has_argument then args.shift() else true
matched_rule: yes
break
throw new Error "unrecognized option: " + arg if is_option and not matched_rule
throw new Error "unrecognized option: $arg" if is_option and not matched_rule
options.arguments.push arg unless is_option
options
# Return the help text for this OptionParser, for --help and such.
# Return the help text for this **OptionParser**, listing and describing all
# of the valid options, for `--help` and such.
help: ->
lines: ['Available options:']
lines.unshift @banner + '\n' if @banner
lines.unshift "$@banner\n" if @banner
for rule in @rules
spaces: 15 - rule.flag.length
spaces: 15 - rule.long_flag.length
spaces: if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
let_part: if rule.letter then rule.letter + ', ' else ' '
lines.push ' ' + let_part + rule.flag + spaces + rule.description
lines.join('\n')
let_part: if rule.short_flag then rule.short_flag + ', ' else ' '
lines.push " $let_part$rule.long_flag$spaces$rule.description"
"\n${ lines.join('\n') }\n"
# Helpers
# -------
# Regex matchers for option flags.
LONG_FLAG: /^(--\w[\w\-]+)/
@@ -42,26 +55,28 @@ SHORT_FLAG: /^(-\w)/
MULTI_FLAG: /^-(\w{2,})/
OPTIONAL: /\[(.+)\]/
# Build rules from a list of valid switch tuples in the form:
# [letter-flag, long-flag, help], or [long-flag, help].
# Build and return the list of option rules. If the optional *short-flag* is
# unspecified, leave it out by padding with `null`.
build_rules: (rules) ->
for tuple in rules
tuple.unshift null if tuple.length < 3
build_rule tuple...
# Build a rule from a short-letter-flag, long-form-flag, and help text.
build_rule: (letter, flag, description) ->
match: flag.match(OPTIONAL)
flag: flag.match(LONG_FLAG)[1]
# Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the
# description of what the option does.
build_rule: (short_flag, long_flag, description) ->
match: long_flag.match(OPTIONAL)
long_flag: long_flag.match(LONG_FLAG)[1]
{
name: flag.substr 2
letter: letter
flag: flag
name: long_flag.substr 2
short_flag: short_flag
long_flag: long_flag
description: description
has_argument: !!(match and match[1])
}
# Normalize arguments by expanding merged flags into multiple flags.
# Normalize arguments by expanding merged flags into multiple flags. This allows
# you to have `-wl` be the same as `--watch --lint`.
normalize_arguments: (args) ->
args: args.slice 0
result: []

View File

@@ -1,23 +1,33 @@
# A CoffeeScript port/version of the Node.js REPL.
# A very simple Read-Eval-Print-Loop. Compiles one line at a time to JavaScript
# and evaluates it. Good for simple tests, or poking around the **Node.js** API.
# Using it looks like this:
#
# coffee> puts "$num bottles of beer" for num in [99..1]
# Required modules.
coffee: require 'coffee-script'
# Require the **coffee-script** module to get access to the compiler.
CoffeeScript: require './coffee-script'
helpers: require('./helpers').helpers
# Shortcut variables.
# Our prompt.
prompt: 'coffee> '
quit: -> process.exit(0)
# The main REPL function. Called everytime a line of code is entered.
# Attempt to evaluate the command. If there's an exception, print it.
readline: (code) ->
# Quick alias for quitting the REPL.
helpers.extend global, {
quit: -> process.exit(0)
}
# The main REPL function. **run** is called every time a line of code is entered.
# Attempt to evaluate the command. If there's an exception, print it out instead
# of exiting.
run: (buffer) ->
try
val: eval coffee.compile code, {no_wrap: true, globals: true}
val: CoffeeScript.run buffer.toString(), {no_wrap: true, globals: true, source: 'repl'}
p val if val isnt undefined
catch err
puts err.stack or err.toString()
print prompt
# Start up the REPL.
process.stdio.addListener 'data', readline
process.stdio.open()
# Start up the REPL by opening **stdin** and listening for input.
stdin: process.openStdin()
stdin.addListener 'data', run
print prompt

View File

@@ -1,84 +1,69 @@
this.exports: this unless process?
# The CoffeeScript language has a good deal of optional syntax, implicit syntax,
# and shorthand syntax. This can greatly complicate a grammar and bloat
# the resulting parse table. Instead of making the parser handle it all, we take
# a series of passes over the token stream, using this **Rewriter** to convert
# shorthand into the unambiguous long form, add implicit indentation and
# parentheses, balance incorrect nestings, and generally clean things up.
# Tokens that must be balanced.
BALANCED_PAIRS: [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'],
['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'],
['INDEX_START', 'INDEX_END'], ['SOAKED_INDEX_START', 'SOAKED_INDEX_END']]
# Set up exported variables for both Node.js and the browser.
if process?
{helpers}: require('./helpers')
else
this.exports: this
helpers: this.helpers
# Tokens that signal the start of a balanced pair.
EXPRESSION_START: pair[0] for pair in BALANCED_PAIRS
# Import the helpers we need.
{include}: helpers
# Tokens that signal the end of a balanced pair.
EXPRESSION_TAIL: pair[1] for pair in BALANCED_PAIRS
# Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE: ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_TAIL)
# Tokens pairs that, in immediate succession, indicate an implicit call.
IMPLICIT_FUNC: ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END']
IMPLICIT_BLOCK:['->', '=>', '{', '[', ',']
IMPLICIT_END: ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'INDENT', 'OUTDENT']
IMPLICIT_CALL: ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START',
'TRY', 'DELETE', 'TYPEOF', 'SWITCH',
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT',
'@', '->', '=>', '[', '(', '{']
# The inverse mappings of token pairs we're trying to fix up.
INVERSES: {}
for pair in BALANCED_PAIRS
INVERSES[pair[0]]: pair[1]
INVERSES[pair[1]]: pair[0]
# Single-line flavors of block expressions that have unclosed endings.
# The grammar can't disambiguate them, so we insert the implicit indentation.
SINGLE_LINERS: ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN']
SINGLE_CLOSERS: ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN']
# In order to keep the grammar simple, the stream of tokens that the Lexer
# emits is rewritten by the Rewriter, smoothing out ambiguities, mis-nested
# indentation, and single-line flavors of expressions.
# The **Rewriter** class is used by the [Lexer](lexer.html), directly against
# its internal array of tokens.
exports.Rewriter: class Rewriter
# Rewrite the token stream in multiple passes, one logical filter at
# a time. This could certainly be changed into a single pass through the
# stream, with a big ol' efficient switch, but it's much nicer like this.
# stream, with a big ol' efficient switch, but it's much nicer to work with
# like this. The order of these passes matters -- indentation must be
# corrected before implicit parentheses can be wrapped around blocks of code.
rewrite: (tokens) ->
@tokens: tokens
@adjust_comments()
@remove_leading_newlines()
@remove_mid_expression_newlines()
@move_commas_outside_outdents()
@close_open_calls_and_indexes()
@add_implicit_indentation()
@add_implicit_parentheses()
@ensure_balance(BALANCED_PAIRS)
@ensure_balance BALANCED_PAIRS
@rewrite_closing_parens()
@tokens
# Rewrite the token stream, looking one token ahead and behind.
# Allow the return value of the block to tell us how many tokens to move
# forwards (or backwards) in the stream, to make sure we don't miss anything
# as the stream changes length under our feet.
# as tokens are inserted and removed, and the stream changes length under
# our feet.
scan_tokens: (block) ->
i: 0
while true
break unless @tokens[i]
move: block(@tokens[i - 1], @tokens[i], @tokens[i + 1], i)
i += move
move: block @tokens[i - 1], @tokens[i], @tokens[i + 1], i
i: + move
true
# Massage newlines and indentations so that comments don't have to be
# correctly indented, or appear on their own line.
# correctly indented, or appear on a line of their own.
adjust_comments: ->
@scan_tokens (prev, token, post, i) =>
return 1 unless token[0] is 'COMMENT'
after: @tokens[i + 2]
[before, after]: [@tokens[i - 2], @tokens[i + 2]]
if after and after[0] is 'INDENT'
@tokens.splice(i + 2, 1)
@tokens.splice(i, 0, after)
@tokens.splice i + 2, 1
if before and before[0] is 'OUTDENT' and post and prev[0] is post[0] is 'TERMINATOR'
@tokens.splice i - 2, 1
else
@tokens.splice i, 0, after
return 1
else if prev and prev[0] isnt 'TERMINATOR' and prev[0] isnt 'INDENT' and prev[0] isnt 'OUTDENT'
@tokens.splice(i, 0, ['TERMINATOR', "\n", prev[2]])
@tokens.splice i, 0, ['TERMINATOR', "\n", prev[2]]
return 2
else
return 1
@@ -86,46 +71,40 @@ exports.Rewriter: class Rewriter
# Leading newlines would introduce an ambiguity in the grammar, so we
# dispatch them here.
remove_leading_newlines: ->
@tokens.shift() if @tokens[0][0] is 'TERMINATOR'
@tokens.shift() while @tokens[0] and @tokens[0][0] is 'TERMINATOR'
# Some blocks occur in the middle of expressions -- when we're expecting
# this, remove their trailing newlines.
remove_mid_expression_newlines: ->
@scan_tokens (prev, token, post, i) =>
return 1 unless post and EXPRESSION_CLOSE.indexOf(post[0]) >= 0 and token[0] is 'TERMINATOR'
@tokens.splice(i, 1)
return 1 unless post and include(EXPRESSION_CLOSE, post[0]) and token[0] is 'TERMINATOR'
@tokens.splice i, 1
return 0
# Make sure that we don't accidentally break trailing commas, which need
# to go on the outside of expression closers.
move_commas_outside_outdents: ->
@scan_tokens (prev, token, post, i) =>
@tokens.splice(i, 1, token) if token[0] is 'OUTDENT' and prev[0] is ','
return 1
# We've tagged the opening parenthesis of a method call, and the opening
# bracket of an indexing operation. Match them with their close.
# The lexer has tagged the opening parenthesis of a method call, and the
# opening bracket of an indexing operation. Match them with their paired
# close.
close_open_calls_and_indexes: ->
parens: [0]
brackets: [0]
@scan_tokens (prev, token, post, i) =>
switch token[0]
when 'CALL_START' then parens.push(0)
when 'INDEX_START' then brackets.push(0)
when '(' then parens[parens.length - 1] += 1
when '[' then brackets[brackets.length - 1] += 1
when 'CALL_START' then parens.push 0
when 'INDEX_START' then brackets.push 0
when '(' then parens[parens.length - 1]: + 1
when '[' then brackets[brackets.length - 1]: + 1
when ')'
if parens[parens.length - 1] is 0
parens.pop()
token[0]: 'CALL_END'
else
parens[parens.length - 1] -= 1
parens[parens.length - 1]: - 1
when ']'
if brackets[brackets.length - 1] == 0
brackets.pop()
token[0]: 'INDEX_END'
else
brackets[brackets.length - 1] -= 1
brackets[brackets.length - 1]: - 1
return 1
# Methods may be optionally called without parentheses, for simple cases.
@@ -133,84 +112,112 @@ exports.Rewriter: class Rewriter
# deal with them.
add_implicit_parentheses: ->
stack: [0]
close_calls: (i) =>
for tmp in [0...stack[stack.length - 1]]
@tokens.splice(i, 0, ['CALL_END', ')', @tokens[i][2]])
size: stack[stack.length - 1] + 1
stack[stack.length - 1]: 0
size
@scan_tokens (prev, token, post, i) =>
tag: token[0]
stack.push(0) if tag is 'INDENT'
if tag is 'OUTDENT'
last: stack.pop()
stack[stack.length - 1] += last
if IMPLICIT_END.indexOf(tag) >= 0 or !post?
return 1 if tag is 'INDENT' and prev and IMPLICIT_BLOCK.indexOf(prev[0]) >= 0
if stack[stack.length - 1] > 0 or tag is 'INDENT'
idx: if tag is 'OUTDENT' then i + 1 else i
stack_pointer: if tag is 'INDENT' then 2 else 1
for tmp in [0...stack[stack.length - stack_pointer]]
@tokens.splice(idx, 0, ['CALL_END', ')', token[2]])
size: stack[stack.length - stack_pointer] + 1
stack[stack.length - stack_pointer]: 0
stack[stack.length - 2]: + stack.pop() if tag is 'OUTDENT'
open: stack[stack.length - 1] > 0
if prev and prev.spaced and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, tag)
@tokens.splice i, 0, ['CALL_START', '(', token[2]]
stack[stack.length - 1]: + 1
stack.push 0 if include(EXPRESSION_START, tag)
return 2
if include(EXPRESSION_START, tag)
if tag is 'INDENT' and !token.generated and open and not (prev and include(IMPLICIT_BLOCK, prev[0]))
size: close_calls(i)
stack.push 0
return size
return 1 unless prev and IMPLICIT_FUNC.indexOf(prev[0]) >= 0 and IMPLICIT_CALL.indexOf(tag) >= 0
@tokens.splice(i, 0, ['CALL_START', '(', token[2]])
stack[stack.length - 1] += 1
return 2
stack.push 0
return 1
if open and !token.generated and (!post or include(IMPLICIT_END, tag))
j: 1; j++ while (nx: @tokens[i + j])? and include(IMPLICIT_END, nx[0])
if nx? and nx[0] is ','
@tokens.splice(i, 1) if tag is 'TERMINATOR'
else
size: close_calls(i)
stack.pop() if tag isnt 'OUTDENT' and include EXPRESSION_END, tag
return size
if tag isnt 'OUTDENT' and include EXPRESSION_END, tag
stack[stack.length - 2]: + stack.pop()
return 1
return 1
# Because our grammar is LALR(1), it can't handle some single-line
# expressions that lack ending delimiters. Use the lexer to add the implicit
# blocks, so it doesn't need to.
# ')' can close a single-line block, but we need to make sure it's balanced.
# 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.
add_implicit_indentation: ->
@scan_tokens (prev, token, post, i) =>
return 1 unless SINGLE_LINERS.indexOf(token[0]) >= 0 and post[0] isnt 'INDENT' and
return 1 unless include(SINGLE_LINERS, token[0]) and
post[0] isnt 'INDENT' and
not (token[0] is 'ELSE' and post[0] is 'IF')
starter: token[0]
@tokens.splice(i + 1, 0, ['INDENT', 2, token[2]])
indent: ['INDENT', 2, token[2]]
indent.generated: true
@tokens.splice i + 1, 0, indent
idx: i + 1
parens: 0
while true
idx += 1
idx: + 1
tok: @tokens[idx]
pre: @tokens[idx - 1]
if (not tok or
(SINGLE_CLOSERS.indexOf(tok[0]) >= 0 and tok[1] isnt ';') or
(tok[0] is ')' && parens is 0)) and
(include(SINGLE_CLOSERS, tok[0]) and tok[1] isnt ';') or
(tok[0] is ')' and parens is 0)) and
not (starter is 'ELSE' and tok[0] is 'ELSE')
insertion: if pre[0] is "," then idx - 1 else idx
@tokens.splice(insertion, 0, ['OUTDENT', 2, token[2]])
outdent: ['OUTDENT', 2, token[2]]
outdent.generated: true
@tokens.splice insertion, 0, outdent
break
parens += 1 if tok[0] is '('
parens -= 1 if tok[0] is ')'
parens: + 1 if tok[0] is '('
parens: - 1 if tok[0] is ')'
return 1 unless token[0] is 'THEN'
@tokens.splice(i, 1)
@tokens.splice i, 1
return 0
# Ensure that all listed pairs of tokens are correctly balanced throughout
# the course of the token stream.
ensure_balance: (pairs) ->
levels: {}
open_line: {}
@scan_tokens (prev, token, post, i) =>
for pair in pairs
[open, close]: pair
levels[open] ||= 0
levels[open] += 1 if token[0] is open
levels[open] -= 1 if token[0] is close
throw new Error("too many " + token[1]) if levels[open] < 0
levels[open]: or 0
if token[0] is open
open_line[open]: token[2] if levels[open] == 0
levels[open]: + 1
levels[open]: - 1 if token[0] is close
throw new Error("too many ${token[1]} on line ${token[2] + 1}") if levels[open] < 0
return 1
unclosed: key for key, value of levels when value > 0
throw new Error("unclosed " + unclosed[0]) if unclosed.length
if unclosed.length
open: unclosed[0]
line: open_line[open] + 1
throw new Error "unclosed $open on line $line"
# We'd like to support syntax like this:
# el.click((event) ->
# el.hide())
#
# el.click((event) ->
# el.hide())
#
# In order to accomplish this, move outdents that follow closing parens
# inwards, safely. The steps to accomplish this are:
#
# 1. Check that all paired tokens are balanced and in order.
# 2. Rewrite the stream with a stack: if you see an '(' or INDENT, add it
# to the stack. If you see an ')' or OUTDENT, pop the stack and replace
# 2. Rewrite the stream with a stack: if you see an `EXPRESSION_START`, add it
# to the stack. If you see an `EXPRESSION_END`, pop the stack and replace
# it with the inverse of what we've just popped.
# 3. Keep track of "debt" for tokens that we fake, to make sure we end
# 3. Keep track of "debt" for tokens that we manufacture, to make sure we end
# up balanced in the end.
#
# 4. Be careful not to alter array or parentheses delimiters with overzealous
# rewriting.
rewrite_closing_parens: ->
stack: []
debt: {}
@@ -218,29 +225,71 @@ exports.Rewriter: class Rewriter
@scan_tokens (prev, token, post, i) =>
tag: token[0]
inv: INVERSES[token[0]]
# Push openers onto the stack.
if EXPRESSION_START.indexOf(tag) >= 0
stack.push(token)
if include EXPRESSION_START, tag
stack.push token
return 1
# The end of an expression, check stack and debt for a pair.
else if EXPRESSION_TAIL.indexOf(tag) >= 0
# If the tag is already in our debt, swallow it.
else if include EXPRESSION_END, tag
if debt[inv] > 0
debt[inv] -= 1
@tokens.splice(i, 1)
debt[inv]: - 1
@tokens.splice i, 1
return 0
else
# Pop the stack of open delimiters.
match: stack.pop()
mtag: match[0]
# Continue onwards if it's the expected tag.
if tag is INVERSES[mtag]
return 1
oppos: INVERSES[mtag]
return 1 if tag is oppos
debt[mtag]: + 1
val: [oppos, if mtag is 'INDENT' then match[1] else oppos]
if @tokens[i + 2]?[0] is mtag
@tokens.splice i + 3, 0, val
stack.push(match)
else
# Unexpected close, insert correct close, adding to the debt.
debt[mtag] += 1
val: if mtag is 'INDENT' then match[1] else INVERSES[mtag]
@tokens.splice(i, 0, [INVERSES[mtag], val])
return 1
@tokens.splice i, 0, val
return 1
else
return 1
# Constants
# ---------
# List of the token pairs that must be balanced.
BALANCED_PAIRS: [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'],
['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'],
['INDEX_START', 'INDEX_END'], ['SOAKED_INDEX_START', 'SOAKED_INDEX_END']]
# The inverse mappings of `BALANCED_PAIRS` we're trying to fix up, so we can
# look things up from either end.
INVERSES: {}
for pair in BALANCED_PAIRS
INVERSES[pair[0]]: pair[1]
INVERSES[pair[1]]: pair[0]
# The tokens that signal the start of a balanced pair.
EXPRESSION_START: pair[0] for pair in BALANCED_PAIRS
# The tokens that signal the end of a balanced pair.
EXPRESSION_END: pair[1] for pair in BALANCED_PAIRS
# Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE: ['CATCH', 'WHEN', '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', '<-', '@']
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
IMPLICIT_CALL: ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START',
'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'EXTENSION',
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT',
'THIS', 'NULL',
'@', '->', '=>', '[', '(', '{']
# Tokens indicating that the implicit call must enclose a block of expressions.
IMPLICIT_BLOCK: ['->', '=>', '{', '[', ',']
# Tokens that always mark the end of an implicit call for single-liners.
IMPLICIT_END: ['IF', 'UNLESS', 'FOR', 'WHILE', 'UNTIL', 'TERMINATOR', 'INDENT'].concat EXPRESSION_END
# Single-line flavors of block expressions that have unclosed endings.
# The grammar can't disambiguate them, so we insert the implicit indentation.
SINGLE_LINERS: ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN']
SINGLE_CLOSERS: ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN']

View File

@@ -1,40 +1,56 @@
# The **Scope** class regulates lexical scoping within CoffeeScript. As you
# generate code, you create a tree of scopes in the same shape as the nested
# function bodies. Each scope knows about the variables declared within it,
# and has a reference to its parent enclosing scope. In this way, we know which
# variables are new and need to be declared with `var`, and which are shared
# with the outside.
# Set up exported variables for both **Node.js** and the browser.
this.exports: this unless process?
# Scope objects form a tree corresponding to the shape of the function
# definitions present in the script. They provide lexical scope, to determine
# whether a variable has been seen before or if it needs to be declared.
#
# Initialize a scope with its parent, for lookups up the chain,
# as well as the Expressions body where it should declare its variables,
# and the function that it wraps.
exports.Scope: class Scope
# The top-level **Scope** object.
@root: null
# Initialize a scope with its parent, for lookups up the chain,
# as well as a reference to the **Expressions** node is belongs to, which is
# where it should declare its variables, and a reference to the function that
# it wraps.
constructor: (parent, expressions, method) ->
[@parent, @expressions, @method]: [parent, expressions, method]
@variables: {}
@temp_var: if @parent then @parent.temp_var else '_a'
if @parent
@temp_var: @parent.temp_var
else
Scope.root: this
@temp_var: '_a'
# Look up a variable in lexical scope, or declare it if not found.
# Look up a variable name in lexical scope, and declare it if it does not
# already exist.
find: (name) ->
return true if @check name
@variables[name]: 'var'
false
# Define a local variable as originating from a parameter in current scope
# -- no var required.
# Test variables and return true the first time fn(v, k) returns true
any: (fn) ->
for v, k of @variables when fn(v, k)
return true
return false
# Reserve a variable name as originating from a function parameter for this
# scope. No `var` required for internal references.
parameter: (name) ->
@variables[name]: 'param'
# Just check to see if a variable has already been declared.
# Just check to see if a variable has already been declared, without reserving.
check: (name) ->
return true if @variables[name]
!!(@parent and @parent.check(name))
# You can reset a found variable on the immediate scope.
reset: (name) ->
delete @variables[name]
# Find an available, short, name for a compiler-generated variable.
# If we need to store an intermediate result, find an available name for a
# compiler-generated variable. `_a`, `_b`, and so on...
free_variable: ->
while @check @temp_var
ordinal: 1 + parseInt @temp_var.substr(1), 36
@@ -42,35 +58,34 @@ exports.Scope: class Scope
@variables[@temp_var]: 'var'
@temp_var
# Ensure that an assignment is made at the top of scope (or top-level
# scope, if requested).
assign: (name, value, top_level) ->
return @parent.assign(name, value, top_level) if top_level and @parent
# Ensure that an assignment is made at the top of this scope
# (or at the top-level scope, if requested).
assign: (name, value) ->
@variables[name]: {value: value, assigned: true}
# Does this scope reference any variables that need to be declared in the
# given function body?
has_declarations: (body) ->
body is @expressions and @declared_variables().length
body is @expressions and @any (k, val) -> val is 'var'
# Does this scope reference any assignments that need to be declared at the
# top of the given function body?
has_assignments: (body) ->
body is @expressions and @assigned_variables().length
body is @expressions and @any (k, val) -> val.assigned
# Return the list of variables first declared in current scope.
# Return the list of variables first declared in this scope.
declared_variables: ->
(key for key, val of @variables when val is 'var').sort()
# Return the list of variables that are supposed to be assigned at the top
# of scope.
# Return the list of assignments that are supposed to be made at the top
# of this scope.
assigned_variables: ->
key + ' = ' + val.value for key, val of @variables when val.assigned
"$key = $val.value" for key, val of @variables when val.assigned
# Compile the string representing all of the declared variables for this scope.
# Compile the JavaScript for all of the variable declarations in this scope.
compiled_declarations: ->
@declared_variables().join ', '
# Compile the string performing all of the variable assignments for this scope.
# Compile the JavaScript for all of the variable assignments in this scope.
compiled_assignments: ->
@assigned_variables().join ', '

View File

@@ -4,31 +4,22 @@ area: (x, y, x1, y1) ->
x: y: 10
x1: y1: 20
ok area(x, y, x1, y1) is 100, 'basic arguments'
ok area(x, y, x1, y1) is 100
ok(area(x, y,
x1, y1) is 100, 'arguments on split lines')
x1, y1) is 100)
ok(area(
x
y
x1
y1
) is 100, 'newline delimited arguments')
) is 100)
curried: ->
ok area.apply(this, arguments.concat(20, 20)) is 100, 'arguments converted into an array'
sum_of_args: ->
sum: 0
sum: + val for val in arguments
sum
curried 10, 10
func: ->
arguments: 25
arguments
ok func(100) is 25, 'arguments as a regular identifier'
this.arguments: 10
ok @arguments is 10, 'arguments accessed as a property'
ok sum_of_args(1, 2, 3, 4, 5) is 15

View File

@@ -1,42 +0,0 @@
nums: n * n for n in [1, 2, 3] when n % 2 isnt 0
results: n * 2 for n in nums
ok results.join(',') is '2,18', 'basic array comprehension'
obj: {one: 1, two: 2, three: 3}
names: prop + '!' for prop of obj
odds: prop + '!' for prop, value of obj when value % 2 isnt 0
ok names.join(' ') is "one! two! three!", 'basic object comprehension'
ok odds.join(' ') is "one! three!", 'object comprehension with a filter'
evens: for num in [1, 2, 3, 4, 5, 6] when num % 2 is 0
num *= -1
num -= 2
num * -1
ok evens.join(', ') is '4, 6, 8', 'multiline array comprehension with filter'
ok 2 in evens, 'the in operator still works, standalone'
# Ensure that the closure wrapper preserves local variables.
obj: {}
methods: ['one', 'two', 'three']
for method in methods
name: method
obj[name]: ->
"I'm " + name
ok obj.one() is "I'm one"
ok obj.two() is "I'm two"
ok obj.three() is "I'm three"
array: [0..10]
ok(num % 2 is 0 for num in array by 2, 'naked ranges are expanded into arrays')

View File

@@ -1,3 +1,4 @@
# Can assign the result of a try/catch block.
result: try
nonexistent * missing
catch error
@@ -5,22 +6,24 @@ catch error
result2: try nonexistent * missing catch error then true
ok result is true and result2 is true, 'can assign the result of a try/catch block'
ok result is true and result2 is true
# Can assign a conditional statement.
get_x: -> 10
if x: get_x() then 100
ok x is 10, 'can assign a conditional statement'
ok x is 10
x: if get_x() then 100
ok x is 100, 'can assign a conditional statement'
ok x is 100
# This-assignment.
tester: ->
@example: -> puts 'example function'
@example: -> 'example function'
this
ok tester().example.name is 'example'
ok tester().example() is 'example function'

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