Compare commits

...

75 Commits
0.2.4 ... 0.3.0

Author SHA1 Message Date
Jeremy Ashkenas
ca0a65ab95 updating documentation for 0.3 2010-01-26 23:23:59 -05:00
Jeremy Ashkenas
3524d618d8 adding unary plus, new version of Underscore, still passes tests. Rebuilt Narwhal 2010-01-26 22:14:18 -05:00
Jeremy Ashkenas
386d3dd307 complete implicit functions, I think these are done. 2010-01-26 21:15:56 -05:00
Jeremy Ashkenas
e998a81b63 removing block literals in favor of implicit calls 2010-01-26 21:05:26 -05:00
Jeremy Ashkenas
aa93d3c387 first draft of whitespace-sensitive method calls and indexes. 2010-01-26 20:59:52 -05:00
Jeremy Ashkenas
ab4a4a5580 make nested implicit indentation just a little bit smarter about outdents and stack levels 2010-01-26 14:49:33 -05:00
Jeremy Ashkenas
3775f682de updated textmate highlighter for new function literal syntax 2010-01-26 10:54:49 -05:00
Jeremy Ashkenas
a9f016e292 trying out new arrows for function literals -> is a function, => is a bound function 2010-01-26 10:52:05 -05:00
Jeremy Ashkenas
55df898112 adding bound functions to the list of implicit call activator tokens 2010-01-26 10:41:28 -05:00
Jeremy Ashkenas
fb7fd53bdf enabling passed functions to fire implicit method calls 2010-01-26 02:27:19 -05:00
Jeremy Ashkenas
29e4043f26 tests passing with smarter block syntax with optional parens 2010-01-26 02:15:08 -05:00
Jeremy Ashkenas
460b3f6d8e first draft of mandatory parentheses around function definition param lists -- all tests pass 2010-01-26 00:40:58 -05:00
Jeremy Ashkenas
63b44a2b03 odd and even were backwards 2010-01-25 22:44:36 -05:00
Jeremy Ashkenas
8efcaf6eec moved CoffeeScript.tmbundle to extras and rewrote the installation instructions in plain text 2010-01-25 22:22:39 -05:00
Jeremy Ashkenas
a732e578ea Merge branch 'master' of git://github.com/olsonjeffery/coffee-script 2010-01-25 22:03:12 -05:00
Jeremy Ashkenas
d6e206b420 adding line number info to unclosed parens, objects, arrays, and indents 2010-01-25 21:07:18 -05:00
Jeremy Ashkenas
91e703052c fixing chained single-line if-elses with a smarter rewriter. 2010-01-25 20:52:33 -05:00
Jeffery Olson
f393b1c897 adding vim syntax file in a new "extras" folder
- also added a VIM-SYNTAX-HOWTO.md readme file
2010-01-25 07:48:06 -08:00
Jeremy Ashkenas
2875de5e73 changed the docs for optional parens 2010-01-25 00:14:00 -05:00
Jeremy Ashkenas
8d63d269b8 making all postfix forms close out implicit calls, as in Ruby 2010-01-24 23:56:27 -05:00
Jeremy Ashkenas
a5d39efdd2 converted the tests to use optional parentheses -- lot's of little subtleties to work out 2010-01-24 23:40:45 -05:00
Jeremy Ashkenas
70e3a6ef2f first draft of optional parentheses, with a couple tests ... more to follow 2010-01-24 22:32:06 -05:00
Jeremy Ashkenas
4b267b401a another poignant example 2010-01-24 20:04:28 -05:00
Jeremy Ashkenas
e6f010b983 adding more examples to the computer_science folder, and fiddling with operator precedence 2010-01-24 18:59:29 -05:00
Jeremy Ashkenas
af53a04932 added test for lexical scope sharing through generated closure wrappers, something uncommonly used, but that was a regression 2010-01-24 13:39:27 -05:00
Jeremy Ashkenas
817e8deb27 adding soaked method calls, with caching 2010-01-24 12:52:15 -05:00
Jeremy Ashkenas
d728c3d669 added existence chains with '?.' -- soaks up attempts to access undefined properties, returning 'undefined' 2010-01-23 23:30:55 -05:00
Jeremy Ashkenas
9160500e84 removing 'this' rewriting in favor of correctly calling generated closures 2010-01-23 21:11:27 -05:00
Jeremy Ashkenas
c3ce2ea9b1 added automatic safety closure wrapper for functions declared within for loops. 2010-01-23 17:53:07 -05:00
Jeremy Ashkenas
5f94186b40 adding the compiled parser back into the repo (after all that) so that it can be used as the source for the narwhal package 2010-01-23 12:44:36 -05:00
Jeremy Ashkenas
791d874058 fixing comments as the last line of a block 2010-01-20 20:36:31 -05:00
Jeremy Ashkenas
a8ae37a428 fixing bug with multiple linebreaks in heredocs 2010-01-19 09:49:23 -05:00
Jeremy Ashkenas
b9c09bfa4e doc updates -- widened the code segments for the sake of the JavaScript 2010-01-17 18:33:31 -05:00
Jeremy Ashkenas
63c9b5c2f0 CoffeeScript 0.2.6 is on the books 2010-01-17 18:12:59 -05:00
Jeremy Ashkenas
80fbe02fda ignoring the top-down parser that doesn't work 2010-01-17 17:41:38 -05:00
Jeremy Ashkenas
e514a39dd2 added binary search example -- chapter 6 of beautiful code 2010-01-17 16:18:24 -05:00
Jeremy Ashkenas
4a32c58221 added bentley's chapter from beautiful code to the examples/tests -- quicksort runtime analysis 2010-01-17 15:58:44 -05:00
Jeremy Ashkenas
4609ad78c2 added the first chapter of beautiful code as a coffeescript example 2010-01-17 15:36:46 -05:00
Jeremy Ashkenas
2d90a751f7 edits for clarity 2010-01-17 14:55:06 -05:00
Jeremy Ashkenas
8647b54a61 rename compile_double_reference to compile_reference 2010-01-17 14:26:00 -05:00
Jeremy Ashkenas
8e1f3c0eca generating multiple calls to the same function should use compile_double_reference to ensure a single evaluation of the call itself. 2010-01-17 14:23:41 -05:00
Jeremy Ashkenas
c4d0903e6a fixing assignment-in-condition 2010-01-17 10:40:59 -05:00
Jeremy Ashkenas
e72ef1a61a reverting change 2010-01-17 10:28:04 -05:00
Jeremy Ashkenas
d7d9cb8d28 only let returns stop an expression from being closure-ified -- breaks and continues may be valid 2010-01-17 10:21:24 -05:00
Jeremy Ashkenas
f6c8e81ea6 the existential operator can now be used infix as well 2010-01-16 23:03:54 -05:00
Jeremy Ashkenas
52539ae7d2 abbreviating the existential operator 2010-01-16 22:26:34 -05:00
Jeremy Ashkenas
95b362499f added the conditional existence operator 2010-01-16 22:17:55 -05:00
Jeremy Ashkenas
0bc4da2b51 ensure that functions are only called once, when chaining comparators 2010-01-16 22:04:08 -05:00
Jeremy Ashkenas
9679fc0b52 removing redundant unary check 2010-01-16 16:49:03 -05:00
Jeremy Ashkenas
9cb0564972 added Python's chainable comparisons, like: 10 > 5 > 1 2010-01-16 16:37:49 -05:00
Jeremy Ashkenas
c6c0c7d059 simplification of function and prototype naming -- last_assign, immediate_assign, and proto_assign are gone, in favor of 'name' and 'proto' properties on CodeNodes 2010-01-16 15:44:07 -05:00
Jeremy Ashkenas
62e946b8ce purely empty functions at the top level should be wrapped in parens, so as not to cause a JS syntax error 2010-01-16 15:02:04 -05:00
Jeremy Ashkenas
6c782b7723 fixes for syntax highlighting assignments and regexes 2010-01-16 14:28:42 -05:00
Jeremy Ashkenas
9eff443032 arguments no longer is just a find-and-replace -- it'll fix the arguments variable at the top of scope if you use it in a function body 2010-01-16 12:52:26 -05:00
Jeremy Ashkenas
8957feedb4 expression closure wrappers are now safer -- they won't be generated if there's a statement_only inside 2010-01-16 12:10:31 -05:00
Jeremy Ashkenas
1cd7fa8ebe added children macro to Node, using it so that all nodes now have a 'children' method -- used for safe references to 'this' within closure wrappers 2010-01-16 11:24:10 -05:00
Jeremy Ashkenas
701cdb4c13 never try to push a statement_only 2010-01-15 19:47:16 -05:00
Jeremy Ashkenas
8dc5da9cc9 adding coffee-haml-filter to the resources section 2010-01-14 14:44:03 -05:00
Jeremy Ashkenas
001cc29deb slightly shorter generated code for ==> 2010-01-14 08:55:09 -05:00
Jeremy Ashkenas
e77e520607 CoffeeScript 0.2.5 is on the books 2010-01-13 23:24:45 -05:00
Jeremy Ashkenas
ed8a54995d with splats allowed in destructuring assignment 2010-01-13 22:25:58 -05:00
Jeremy Ashkenas
2d206e7b60 pulling out pushes into a pushnode 2010-01-13 21:33:46 -05:00
Jeremy Ashkenas
bb9fdd3015 while loops can now be used as expressions -- they return an array containing the computed result of each iteration. 2010-01-13 21:27:22 -05:00
Jeremy Ashkenas
1e7d638435 adding bound functions, with test 2010-01-13 20:59:57 -05:00
Jeremy Ashkenas
0ceca0778c adding when clauses with multiple values 2010-01-13 19:56:35 -05:00
Jeremy Ashkenas
abd9ab5c71 unified ParamSplatNode and ArgSplatNode into SplatNode 2010-01-12 23:49:47 -05:00
Jeremy Ashkenas
ea349a1a59 more safety type-checks in nodes.rb 2010-01-12 23:26:35 -05:00
Jeremy Ashkenas
f0d5db7e66 fixing heredocs to use the left-most indent as the indentation guide -- not just the first line of the heredoc 2010-01-12 23:06:12 -05:00
Jeremy Ashkenas
914ba1c244 removing commented-out bit 2010-01-12 18:01:12 -05:00
Jeremy Ashkenas
844ea33274 mistaken commit 2010-01-12 17:45:06 -05:00
Jeremy Ashkenas
87e04e9952 nicer syntax error messages for newlines and indentation 2010-01-12 17:44:37 -05:00
Jeremy Ashkenas
197914bcf7 nicer syntax error messages for newlines and indentation 2010-01-12 17:44:03 -05:00
Jeremy Ashkenas
8dfbd1a2a8 using Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty, with an alias, for Rhino and java objects 2010-01-12 17:35:37 -05:00
Jeremy Ashkenas
c19647ad33 adding and fixing test for empty strings 2010-01-12 08:52:44 -05:00
Jeremy Ashkenas
27f7ef09af allow leading newlines in coffee scripts 2010-01-12 08:49:39 -05:00
115 changed files with 5137 additions and 1386 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
presentation
test.coffee
parser.output
lib/coffee_script/parser.rb
test/fixtures/underscore
examples/beautiful_code/parse.coffee
*.gem

View File

@@ -25,7 +25,7 @@ namespace :build do
desc "Compile and install the Ultraviolet syntax highlighter"
task :ultraviolet do
sh "plist2syntax lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage"
sh "plist2syntax extras/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage"
sh "sudo mv coffeescript.yaml /usr/local/lib/ruby/gems/1.8/gems/ultraviolet-0.10.2/syntax/coffeescript.syntax"
end
@@ -58,6 +58,8 @@ namespace :gem do
desc 'Build and install the coffee-script gem'
task :install do
verbose = "lib/coffee_script/parser.output"
FileUtils.rm(verbose) if File.exists?(verbose)
sh "gem build coffee-script.gemspec"
sh "sudo gem install #{Dir['*.gem'].join(' ')} --local --no-ri --no-rdoc"
end

View File

@@ -1,7 +1,7 @@
Gem::Specification.new do |s|
s.name = 'coffee-script'
s.version = '0.2.4' # Keep version in sync with coffee-script.rb
s.date = '2010-1-12'
s.version = '0.3.0' # Keep version in sync with coffee-script.rb
s.date = '2010-1-26'
s.homepage = "http://jashkenas.github.com/coffee-script/"
s.summary = "The CoffeeScript Compiler"
@@ -22,5 +22,6 @@ Gem::Specification.new do |s|
s.require_paths = ['lib']
s.executables = ['coffee']
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README', 'package.json']
s.files = Dir['bin/*', 'examples/*', 'extras/**/*', 'lib/**/*',
'coffee-script.gemspec', 'LICENSE', 'README', 'package.json']
end

View File

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

View File

@@ -4,4 +4,4 @@ lunch: eat(food) for food in ['toast', 'cheese', 'wine']
# Naive collision detection.
for roid in asteroids
for roid2 in asteroids when roid isnt roid2
roid.explode() if roid.overlaps(roid2)
roid.explode() if roid.overlaps roid2

View File

@@ -1,4 +0,0 @@
$('table.list').each() table =>
$('tr.account', table).each() row =>
row.show()
row.highlight()

View File

@@ -0,0 +1,5 @@
cholesterol: 127
healthy: 200 > cholesterol > 60

View File

@@ -1 +1,8 @@
solipsism: true if mind? and not world?
solipsism: true if mind? and not world?
speed ?= 140

View File

@@ -1,4 +1,4 @@
grade: student =>
grade: (student) ->
if student.excellent_work
"A+"
else if student.okay_stuff

View File

@@ -2,5 +2,5 @@ alert(
try
nonexistent / undefined
catch error
"Caught an error: " + error
"And the error is ... " + error
)

View File

@@ -0,0 +1,6 @@
Account: (customer, cart) ->
this.customer: customer
this.cart: cart
$('.shopping_cart').bind 'click', (event) =>
this.customer.purchase this.cart

View File

@@ -1,2 +1,2 @@
square: x => x * x
cube: x => square(x) * x
square: (x) -> x * x
cube: (x) -> square(x) * x

View File

@@ -1,5 +1,5 @@
weather_report: location =>
weather_report: (location) ->
# Make an Ajax request to fetch the weather...
[location, 72, "Mostly Sunny"]
[city, temp, forecast]: weather_report("Berkeley, CA")
[city, temp, forecast]: weather_report "Berkeley, CA"

View File

@@ -6,7 +6,7 @@ opposite_day: true
number: -42 if opposite_day
# Functions:
square: x => x * x
square: (x) -> x * x
# Arrays:
list: [1, 2, 3, 4, 5]
@@ -15,15 +15,15 @@ list: [1, 2, 3, 4, 5]
math: {
root: Math.sqrt
square: square
cube: x => x * square(x)
cube: (x) -> x * square x
}
# Splats:
race: winner, runners... =>
print(winner, runners)
race: (winner, runners...) ->
print winner, runners
# Existence:
alert("I knew it!") if elvis?
alert "I knew it!" if elvis?
# Array comprehensions:
cubed_list: math.cube(num) for num in list
cubed_list: math.cube num for num in list

View File

@@ -1,6 +1,6 @@
countdown: num for num in [10..1]
egg_delivery: =>
egg_delivery: ->
for i in [0...eggs.length] by 12
dozen_eggs: eggs[i...i+12]
deliver(new egg_carton(dozen))
deliver new egg_carton(dozen)

View File

@@ -1,5 +1,5 @@
num: 1
change_numbers: =>
change_numbers: ->
new_num: -1
num: 10
new_num: change_numbers()

View File

@@ -0,0 +1 @@
lottery.draw_winner()?.address?.zipcode

View File

@@ -1,6 +1,6 @@
gold: silver: the_field: "unknown"
medalists: first, second, rest... =>
award_medals: (first, second, rest...) ->
gold: first
silver: second
the_field: rest
@@ -18,8 +18,8 @@ contenders: [
"Usain Bolt"
]
medalists(contenders...)
award_medals contenders...
alert("Gold: " + gold)
alert("Silver: " + silver)
alert("The Field: " + the_field)
alert "Gold: " + gold
alert "Silver: " + silver
alert "The Field: " + the_field

View File

@@ -1,21 +1,21 @@
Animal: =>
Animal::move: meters =>
alert(this.name + " moved " + meters + "m.")
Animal: ->
Animal::move: (meters) ->
alert this.name + " moved " + meters + "m."
Snake: name => this.name: name
Snake: (name) -> this.name: name
Snake extends Animal
Snake::move: =>
alert("Slithering...")
super(5)
Snake::move: ->
alert "Slithering..."
super 5
Horse: name => this.name: name
Horse: (name) -> this.name: name
Horse extends Animal
Horse::move: =>
alert("Galloping...")
super(45)
Horse::move: ->
alert "Galloping..."
super 45
sam: new Snake("Sammy the Python")
tom: new Horse("Tommy the Palomino")
sam: new Snake "Sammy the Python"
tom: new Horse "Tommy the Palomino"
sam.move()
tom.move()

View File

@@ -1,9 +1,10 @@
switch day
when "Tuesday" then eat_breakfast()
when "Wednesday" then go_to_the_park()
when "Saturday"
when "Mon" then go_to_work()
when "Tue" then go_to_the_park()
when "Thu" then go_ice_fishing()
when "Fri", "Sat"
if day is bingo_day
go_to_bingo()
go_dancing()
when "Sunday" then go_to_church()
when "Sun" then go_to_church()
else go_to_work()

View File

@@ -2,6 +2,6 @@ try
all_hell_breaks_loose()
cats_and_dogs_living_together()
catch error
print(error)
print error
finally
clean_up()

View File

@@ -1,5 +1,10 @@
while demand > supply
sell()
restock()
# Econ 101
if this.studying_economics
while supply > demand then buy()
while supply < demand then sell()
while supply > demand then buy()
# Nursery Rhyme
num: 6
lyrics: while num -= 1
num + " little monkeys, jumping on the bed.
One fell out and bumped his head."

View File

@@ -6,7 +6,7 @@ body {
font-family: Arial, Helvetica, sans-serif;
}
div.container {
width: 850px;
width: 950px;
margin: 50px 0 50px 50px;
}
p {
@@ -77,7 +77,7 @@ div.code {
}
div.code pre {
float: left;
width: 410px;
width: 450px;
border-left: 1px dotted #559;
padding: 0 0 0 12px;
margin: 0;

View File

@@ -28,6 +28,7 @@ pre.idle .LibraryConstant {
color: #A535AE;
}
pre.idle .FunctionArgument {
color: #0076ad;
}
pre.idle .BuiltInConstant {
color: #A535AE;

View File

@@ -51,7 +51,7 @@
<p>
<b>Latest Version:</b>
<a href="http://gemcutter.org/gems/coffee-script">0.2.4</a>
<a href="http://gemcutter.org/gems/coffee-script">0.3.0</a>
</p>
<h2>Table of Contents</h2>
@@ -65,7 +65,6 @@
<a href="#objects_and_arrays">Objects and Arrays</a><br />
<a href="#lexical_scope">Lexical Scoping and Variable Safety</a><br />
<a href="#conditionals">Conditionals, Ternaries, and Conditional Assignment</a><br />
<a href="#existence">The Existence Operator</a><br />
<a href="#aliases">Aliases</a><br />
<a href="#splats">Splats...</a><br />
<a href="#arguments">Arguments are Arrays</a><br />
@@ -73,15 +72,16 @@
<a href="#comprehensions">Comprehensions (Arrays, Objects, and Ranges)</a><br />
<a href="#slice_splice">Array Slicing and Splicing with Ranges</a><br />
<a href="#expressions">Everything is an Expression</a><br />
<a href="#existence">The Existential Operator</a><br />
<a href="#inheritance">Inheritance, and Calling Super from a Subclass</a><br />
<a href="#blocks">Blocks</a><br />
<a href="#pattern_matching">Pattern Matching</a><br />
<a href="#fat_arrow">Function Binding</a><br />
<a href="#embedded">Embedded JavaScript</a><br />
<a href="#switch">Switch/When/Else</a><br />
<a href="#try">Try/Catch/Finally</a><br />
<a href="#comparisons">Chained Comparisons</a><br />
<a href="#strings">Multiline Strings and Heredocs</a><br />
<a href="#resources">Resources</a><br />
<a href="#contributing">Contributing</a><br />
<a href="#change_log">Change Log</a><br />
</p>
@@ -172,7 +172,7 @@ gem install coffee-script</pre>
<td><code>-e, --eval</code></td>
<td>
Compile and print a little snippet of CoffeeScript directly from the
command line (or from <b>stdin</b>). For example:<br /><tt>coffee -e "square: x => x * x"</tt>
command line (or from <b>stdin</b>). For example:<br /><tt>coffee -e "square: (x) -> x * x"</tt>
</td>
</tr>
<tr>
@@ -186,7 +186,7 @@ gem install coffee-script</pre>
<td><code>-v, --verbose</code></td>
<td>
As the JavaScript is being generated, print out every step of code
generation, including lexical scope and the node in the
generation, including lexical scope and the nodes in the
AST.
</td>
</tr>
@@ -246,20 +246,27 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
use indentation.
</p>
<p>
You don't need to use parentheses to invoke a function, if you're passing
arguments:<br /><tt>print "coffee"</tt>
</p>
<p>
You can use newlines to break up your expression into smaller pieces,
as long as CoffeeScript can tell that the line hasn't finished
(similar to how Ruby handles it). For example,
if the line ends in an operator, dot, or keyword.
as long as CoffeeScript can determine that the line hasn't finished yet.
</p>
<p id="functions">
<b class="header">Functions and Invocation</b>
Functions are defined by a list of parameters, an arrow, and the
function body. The empty function looks like this: <tt>=></tt>. All
functions in CoffeeScript are named, for the benefit of debug messages.
function body. The empty function looks like this: <tt>-></tt>. All
functions in CoffeeScript are named by default, for easier debugging.
</p>
<%= code_for('functions', 'cube(5)') %>
<p>
If you'd like to create an anonymous function, just wrap it in parentheses:
<tt>((x) -> x * x)</tt>
</p>
<p id="assignment">
<b class="header">Assignment</b>
@@ -269,7 +276,7 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('assignment', 'greeting') %>
<p>
Declarations of new variables are pushed up to the top of the nearest
Declaration of new variables are pushed up to the top of the nearest
lexical scope, so that assignment may always be performed within expressions.
</p>
@@ -278,7 +285,7 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
Object and Array literals look very similar to their JavaScript cousins.
When you spread out each assignment on a separate line, the commas are
optional. In this way, assigning object properties looks the same as
assigning local variables, and can be moved around freely. You can mix
assigning local variables, and can be moved around freely. Feel free to mix
and match the two styles.
</p>
<%= code_for('objects_and_arrays', 'song.join(",")') %>
@@ -303,9 +310,14 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
CoffeeScript output is wrapped in an anonymous function:
<tt>(function(){ ... })();</tt> This safety wrapper, combined with the
automatic generation of the <tt>var</tt> keyword, make it exceedingly difficult
to pollute the global namespace by accident. If you'd like to create
global variables, attach them as properties on <b>window</b>,
or on the <b>exports</b> object in CommonJS.
to pollute the global namespace by accident.
</p>
<p>
If you'd like to create top-level variables for other scripts to use,
attach them as properties on <b>window</b>, or on the <b>exports</b>
object in CommonJS. The <b>existential operator</b> (below), gives you a
reliable way to figure out where to add them, if you're targeting both
CommonJS and the browser: <tt>root: exports ? this</tt>
</p>
<p id="conditionals">
@@ -327,16 +339,6 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
truthy variables.
</p>
<p id="existence">
<b class="header">The Existence Operator</b>
It's a little difficult to check for the existence of a variable in
JavaScript. <tt>if (variable) ...</tt> comes close, but fails for zero,
the empty string, and false. The existence operator <tt>?</tt> returns true unless
a variable is <b>null</b> or <b>undefined</b>, which makes it analogous
to Ruby's <tt>nil?</tt>
</p>
<%= code_for('existence') %>
<p id="aliases">
<b class="header">Aliases</b>
Because the <tt>==</tt> operator frequently causes undesirable coercion,
@@ -372,7 +374,7 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
The JavaScript <b>arguments object</b> is a useful way to work with
functions that accept variable numbers of arguments. CoffeeScript provides
splats <tt>...</tt>, both for function definition as well as invocation,
making variable arguments a little bit more palatable.
making variable numbers of arguments a little bit more palatable.
</p>
<%= code_for('splats', true) %>
@@ -387,9 +389,12 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
<p id="while">
<b class="header">While Loops</b>
The only low-level loop that CoffeeScript provides is the while loop.
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
through the loop.
</p>
<%= code_for('while') %>
<%= code_for('while', 'lyrics.join("\n")') %>
<p>
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
@@ -469,6 +474,41 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
into a function call:
</p>
<%= code_for('expressions_try', true) %>
<p>
There are a handful of statements in JavaScript that can't be meaningfully
converted into expressions, namely <tt>break</tt>, <tt>continue</tt>,
and <tt>return</tt>. If you make use of them within a block of code,
CoffeeScript won't try to perform the conversion.
</p>
<p id="existence">
<b class="header">The Existential Operator</b>
It's a little difficult to check for the existence of a variable in
JavaScript. <tt>if (variable) ...</tt> comes close, but fails for zero,
the empty string, and false. CoffeeScript's existential operator <tt>?</tt> returns true unless
a variable is <b>null</b> or <b>undefined</b>, which makes it analogous
to Ruby's <tt>nil?</tt>
</p>
<p>
It can also be used for safer conditional assignment than <tt>||=</tt>
provides, for cases where you may be handling numbers or strings.
</p>
<%= code_for('existence', 'speed') %>
<p>
The accessor variant of the existential operator <tt>?.</tt> can be used to soak
up null references in a chain of properties. Use it instead
of the dot accessor <tt>.</tt> in cases where the base value may be <b>null</b>
or <b>undefined</b>. If all of the properties exist then you'll get the expected
result, if the chain is broken, <b>undefined</b> is returned instead of
the <b>TypeError</b> that would be raised otherwise.
</p>
<%= code_for('soaks') %>
<p>
Soaking up nulls is similar to Ruby's
<a href="http://andand.rubyforge.org/">andand gem</a>, and to the
<a href="http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator%28%3F.%29">safe navigation operator</a>
in Groovy.
</p>
<p id="inheritance">
<b class="header">Inheritance, and Calling Super from a Subclass</b>
@@ -492,20 +532,6 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('super', true) %>
<p id="blocks">
<b class="header">Blocks</b>
Many common looping functions (in Prototype, jQuery, and Underscore,
for example) take a single function as their final argument. To make
final functions easier to pass, CoffeeScript includes block syntax,
so you don't have to close the parentheses on the other side.
</p>
<%= code_for('blocks') %>
<p>
If you prefer not to use blocks, you'll need to add a pair of parentheses
to help distinguish the arguments from the definition of the function:
<tt>_.map(array, (num => num * 2))</tt>
</p>
<p id="pattern_matching">
<b class="header">Pattern Matching (Destructuring Assignment)</b>
To make extracting values from complex arrays and objects more convenient,
@@ -528,6 +554,17 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('object_extraction', 'poet + " — " + street') %>
<p id="fat_arrow">
<b class="header">Function binding</b>
The fat arrow <tt>=></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
to use with <tt>bind</tt>. Functions created with the fat arrow are able to access
properties of the <tt>this</tt> where they're defined.
</p>
<%= code_for('fat_arrow') %>
<p id="embedded">
<b class="header">Embedded JavaScript</b>
Hopefully, you'll never need to use it, but if you ever need to intersperse
@@ -546,6 +583,11 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
in a returnable, assignable expression. The format is: <tt>switch</tt> condition,
<tt>when</tt> clauses, <tt>else</tt> the default case.
</p>
<p>
As in Ruby, <b>switch</b> statements in CoffeeScript can take multiple
values for each <b>when</b> clause. If any of the values match, the clause
runs.
</p>
<%= code_for('switch') %>
<p id="try">
@@ -555,6 +597,15 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('try') %>
<p id="comparisons">
<b class="header">Chained Comparisons</b>
CoffeeScript borrows
<a href="http://docs.python.org/reference/expressions.html#notin">chained comparisons</a>
from Python &mdash; making it easy to test if a value falls within a
certain range.
</p>
<%= code_for('comparisons', 'healthy') %>
<p id="strings">
<b class="header">Multiline Strings and Heredocs</b>
Multiline strings are allowed in CoffeeScript.
@@ -583,6 +634,12 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
<li>
<a href="http://github.com/jashkenas/coffee-script/issues">Bugs, Feature Requests, and General Discussion</a>
</li>
<li>
<a href="http://github.com/creationix/coffeepot">CoffeePot</a><br />
An implementation of CoffeeScript, written in CoffeeScript, by
<a href="http://github.com/creationix">Tim Caswell</a>. Compiles just
a limited subset at this point.
</li>
<li>
<a href="http://github.com/jnicklas/bistro_car">BistroCar</a><br />
A Rails plugin by
@@ -590,41 +647,49 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
that includes CoffeeScript helpers,
bundling and minification.
</li>
</ul>
<h2 id="contributing">Contributing</h2>
<p>
Here's a wish list of things that would be wonderful to have contributed:
</p>
<ul>
<li>
<a href="http://github.com/jashkenas/coffee-script/issues#issue/8">
A CoffeeScript version of the compiler.
</a>
</li>
<li>
Test cases for any syntax errors you encounter that you think CoffeeScript
should be able to compile properly.
</li>
<li>
A tutorial that introduces CoffeeScript from the ground up for folks
without knowledge of JavaScript.
</li>
<li>
Integration with Processing.js's JavaScript API (this would depend on
having a JavaScript version of the compiler).
</li>
<li>
A lot of the code generation in <tt>nodes.rb</tt> gets into messy
string manipulation. Techniques for cleaning this up across the board
would be appreciated.
<a href="http://github.com/inem/coffee-haml-filter">coffee-haml-filter</a><br />
A custom <a href="http://haml-lang.com/">HAML</a> filter, by
<a href="http://github.com/inem">Ivan Nemytchenko</a>, that embeds
snippets of CoffeeScript within your HAML templates.
</li>
</ul>
<h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 20px;">0.3.0</b>
CoffeeScript 0.3 includes major syntax changes:
<br />
The function symbol was changed to
<tt>-></tt>, and the bound function symbol is now <tt>=></tt>.
<br />
Parameter lists in function definitions must now be wrapped in parentheses.
<br />
Added property soaking, with the <tt>?.</tt> operator.
<br />
Made parentheses optional, when invoking functions with arguments.
<br />
Removed the obsolete block literal syntax.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.2.6</b>
Added Python-style chained comparisons, the conditional existence
operator <tt>?=</tt>, and some examples from <i>Beautiful Code</i>.
Bugfixes relating to statement-to-expression conversion, arguments-to-array
conversion, and the TextMate syntax highlighter.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.2.5</b>
The conditions in switch statements can now take multiple values at once &mdash;
If any of them are true, the case will run. Added the long arrow <tt>==></tt>,
which defines and immediately binds a function to <tt>this</tt>. While loops can
now be used as expressions, in the same way that comprehensions can. Splats
can be used within pattern matches to soak up the rest of an array.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.2.4</b>
Added ECMAScript Harmony style destructuring assignment, for dealing with
@@ -668,7 +733,7 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
<b class="header" style="margin-top: 20px;">0.2.0</b>
Major release. Significant whitespace. Better statement-to-expression
conversion. Splats. Splice literals. Object comprehensions. Blocks.
The existence operator. Many thanks to all the folks who posted issues,
The existential operator. Many thanks to all the folks who posted issues,
with special thanks to
<a href="http://github.com/kamatsu">Liam O'Connor-Davis</a> for whitespace
and expression help.

View File

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

View File

@@ -3,18 +3,18 @@
// Eat lunch.
lunch = (function() {
__a = []; __b = ['toast', 'cheese', 'wine'];
for (__c=0; __c<__b.length; __c++) {
for (__c = 0; __c < __b.length; __c++) {
food = __b[__c];
__a.push(eat(food));
}
return __a;
})();
}).call(this);
// Naive collision detection.
__d = asteroids;
for (__e=0; __e<__d.length; __e++) {
for (__e = 0; __e < __d.length; __e++) {
roid = __d[__e];
__f = asteroids;
for (__g=0; __g<__f.length; __g++) {
for (__g = 0; __g < __f.length; __g++) {
roid2 = __f[__g];
if (roid !== roid2) {
if (roid.overlaps(roid2)) {

View File

@@ -1,8 +0,0 @@
(function(){
$('table.list').each(function(table) {
return $('tr.account', table).each(function(row) {
row.show();
return row.highlight();
});
});
})();

View File

@@ -0,0 +1,5 @@
(function(){
var cholesterol, healthy;
cholesterol = 127;
healthy = (200 > cholesterol) && (cholesterol > 60);
})();

View File

@@ -1,5 +1,5 @@
(function(){
var date, mood;
var date, expensive, mood;
if (singing) {
mood = greatly_improved;
}

View File

@@ -1,6 +1,7 @@
(function(){
var solipsism;
var solipsism, speed;
if ((typeof mind !== "undefined" && mind !== null) && !(typeof world !== "undefined" && world !== null)) {
solipsism = true;
}
speed = (typeof speed !== "undefined" && speed !== null) ? speed : 140;
})();

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

@@ -1,13 +1,14 @@
(function(){
var __a, __b, globals, name;
var __hasProp = Object.prototype.hasOwnProperty;
// The first ten global properties.
globals = ((function() {
__a = []; __b = window;
for (name in __b) {
if (__b.hasOwnProperty(name)) {
if (__hasProp.call(__b, name)) {
__a.push(name);
}
}
return __a;
})()).slice(0, 10);
}).call(this)).slice(0, 10);
})();

View File

@@ -3,7 +3,7 @@
try {
return nonexistent / undefined;
} catch (error) {
return "Caught an error: " + error;
return "And the error is ... " + error;
}
})());
}).call(this));
})();

View File

@@ -0,0 +1,17 @@
(function(){
var Account;
Account = function Account(customer, cart) {
var __a;
this.customer = customer;
this.cart = cart;
__a = $('.shopping_cart').bind('click', (function(__this) {
var __func = function(event) {
return this.customer.purchase(this.cart);
};
return (function() {
return __func.apply(__this, arguments);
});
})(this));
return Account === this.constructor ? this : __a;
};
})();

View File

@@ -1,5 +1,6 @@
(function(){
var __a, __b, age, ages, child, years_old;
var __hasProp = Object.prototype.hasOwnProperty;
years_old = {
max: 10,
ida: 9,
@@ -9,10 +10,10 @@
__a = []; __b = years_old;
for (child in __b) {
age = __b[child];
if (__b.hasOwnProperty(child)) {
if (__hasProp.call(__b, child)) {
__a.push(child + " is " + age);
}
}
return __a;
})();
}).call(this);
})();

View File

@@ -34,10 +34,10 @@
// Array comprehensions:
cubed_list = (function() {
__a = []; __b = list;
for (__c=0; __c<__b.length; __c++) {
for (__c = 0; __c < __b.length; __c++) {
num = __b[__c];
__a.push(math.cube(num));
}
return __a;
})();
}).call(this);
})();

View File

@@ -6,7 +6,7 @@
__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;
@@ -14,7 +14,7 @@
__f.push((function() {
dozen_eggs = eggs.slice(i, i + 12);
return deliver(new egg_carton(dozen));
})());
}).call(this));
}
return __f;
};

View File

@@ -0,0 +1,4 @@
(function(){
var __a;
((__a = lottery.draw_winner()) == undefined ? undefined : __a.address == undefined ? undefined : __a.address.zipcode);
})();

View File

@@ -1,7 +1,7 @@
(function(){
var contenders, gold, medalists, silver, the_field;
gold = silver = the_field = "unknown";
medalists = function medalists(first, second) {
var award_medals, contenders, gold, silver, the_field;
gold = (silver = (the_field = "unknown"));
award_medals = function award_medals(first, second) {
var rest;
rest = Array.prototype.slice.call(arguments, 2);
gold = first;
@@ -9,7 +9,7 @@
return the_field = rest;
};
contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];
medalists.apply(this, contenders);
award_medals.apply(this, contenders);
alert("Gold: " + gold);
alert("Silver: " + silver);
alert("The Field: " + the_field);

View File

@@ -1,7 +1,6 @@
(function(){
var Animal, Horse, Snake, __a, __b, sam, tom;
Animal = function Animal() {
};
Animal = function Animal() { };
Animal.prototype.move = function move(meters) {
return alert(this.name + " moved " + meters + "m.");
};

View File

@@ -1,14 +1,16 @@
(function(){
if (day === "Tuesday") {
eat_breakfast();
} else if (day === "Wednesday") {
if (day === "Mon") {
go_to_work();
} else if (day === "Tue") {
go_to_the_park();
} else if (day === "Saturday") {
} else if (day === "Thu") {
go_ice_fishing();
} else if (day === "Fri" || day === "Sat") {
if (day === bingo_day) {
go_to_bingo();
go_dancing();
}
} else if (day === "Sunday") {
} else if (day === "Sun") {
go_to_church();
} else {
go_to_work();

View File

@@ -1,9 +1,22 @@
(function(){
while (demand > supply) {
sell();
restock();
}
while (supply > demand) {
buy();
var __a, lyrics, num;
// Econ 101
if (this.studying_economics) {
while (supply > demand) {
buy();
}
while (supply < demand) {
sell();
}
}
// Nursery Rhyme
num = 6;
lyrics = (function() {
__a = [];
while (num -= 1) {
__a.push(num + " little monkeys, jumping on the bed. \
One fell out and bumped his head.");
}
return __a;
}).call(this);
})();

View File

@@ -14,63 +14,39 @@
var arr = [];
while (num--) arr.push(num);
JSLitmus.test('current comprehensions', function() {
__a = arr;
__c = [];
for (__b in __a) {
if (__a.hasOwnProperty(__b)) {
num = __a[__b];
__d = num;
__c.push(num);
}
}
var f1 = function f1() {
return arr;
};
JSLitmus.test('regular function', function() {
f1();
});
JSLitmus.test('raw for loop (best we can do)', function() {
__a = arr;
__c = new Array(__a.length);
for (__b=0; __b < __a.length; __b++) {
__c[__b] = __a[__b];
}
var __this = this;
var f2 = function f2() {
return (function() {
return arr;
}).apply(__this, arguments);
};
JSLitmus.test('bound function', function() {
f2();
});
JSLitmus.test('current without hasOwnProperty check', function() {
__a = arr;
__c = [];
for (__b in __a) {
num = __a[__b];
__d = num;
__c.push(num);
}
});
JSLitmus.test('raw for..in loop', function() {
__a = arr;
__c = new Array(__a.length);
for (__b in __a) {
__c[__b] = __a[__b];
}
});
JSLitmus.test('weepy\'s comprehensions', function() {
__c = []; __a = arr;
__d = function(num, __b) {
__c.push(num);
var f3 = (function() {
__b = function() {
return arr;
};
if (__a instanceof Array) {
for (__b=0; __b<__a.length; __b++) __d(__a[__b], __b);
} else {
for (__b in __a) { if (__a.hasOwnProperty(__b)) __d(__a[__b], __b); }
}
return (function f2() {
return __b.apply(__this, arguments);
});
})();
JSLitmus.test('prebound function', function() {
f3();
});
JSLitmus.test('CoffeeScript 0.2.2 comprehensions', function() {
__c = []; __a = arr;
for (__b=0; __b<__a.length; __b++) {
num = __a[__b];
__c.push(num);
}
});
</script>
</body>

View File

@@ -19,7 +19,7 @@
<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) 2009 Jeremy Ashkenas, DocumentCloud Inc.</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>
@@ -40,7 +40,7 @@
<span class="line-numbers"> 21 </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"> 22 </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"> 23 </span> <span class="Comment"><span class="Comment">#</span> underscore functions. Wrapped objects may be chained.</span>
<span class="line-numbers"> 24 </span> <span class="FunctionArgument"> wrapper: obj </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 24 </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"> 25 </span> <span class="FunctionName">this._wrapped</span><span class="Keyword">:</span> obj
<span class="line-numbers"> 26 </span> <span class="Variable">this</span>
<span class="line-numbers"> 27 </span>
@@ -50,7 +50,7 @@
<span class="line-numbers"> 31 </span>
<span class="line-numbers"> 32 </span>
<span class="line-numbers"> 33 </span> <span class="Comment"><span class="Comment">#</span> Create a safe reference to the Underscore object forreference below.</span>
<span class="line-numbers"> 34 </span> <span class="FunctionArgument"> _: root._: obj </span><span class="Storage">=&gt;</span> <span class="Keyword">new</span> <span class="TypeName">wrapper</span>(obj)
<span class="line-numbers"> 34 </span> <span class="FunctionName">_</span><span class="Keyword">:</span> <span class="FunctionName">root._</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"> 35 </span>
<span class="line-numbers"> 36 </span>
<span class="line-numbers"> 37 </span> <span class="Comment"><span class="Comment">#</span> Export the Underscore object for CommonJS.</span>
@@ -58,22 +58,22 @@
<span class="line-numbers"> 39 </span>
<span class="line-numbers"> 40 </span>
<span class="line-numbers"> 41 </span> <span class="Comment"><span class="Comment">#</span> Create quick reference variables for speed access to core prototypes.</span>
<span class="line-numbers"> 42 </span> <span class="FunctionName">slice</span><span class="Keyword">:</span> Array<span class="Keyword">:</span><span class="Keyword">:</span>slice
<span class="line-numbers"> 43 </span> <span class="FunctionName">unshift</span><span class="Keyword">:</span> Array<span class="Keyword">:</span><span class="Keyword">:</span>unshift
<span class="line-numbers"> 44 </span> <span class="FunctionName">toString</span><span class="Keyword">:</span> Object<span class="Keyword">:</span><span class="Keyword">:</span>toString
<span class="line-numbers"> 45 </span> <span class="FunctionName">hasOwnProperty</span><span class="Keyword">:</span> Object<span class="Keyword">:</span><span class="Keyword">:</span>hasOwnProperty
<span class="line-numbers"> 46 </span> <span class="FunctionName">propertyIsEnumerable</span><span class="Keyword">:</span> Object<span class="Keyword">:</span><span class="Keyword">:</span>propertyIsEnumerable
<span class="line-numbers"> 42 </span> <span class="FunctionName">slice</span><span class="Keyword">:</span> <span class="FunctionName">Array:</span><span class="Keyword">:</span>slice
<span class="line-numbers"> 43 </span> <span class="FunctionName">unshift</span><span class="Keyword">:</span> <span class="FunctionName">Array:</span><span class="Keyword">:</span>unshift
<span class="line-numbers"> 44 </span> <span class="FunctionName">toString</span><span class="Keyword">:</span> <span class="FunctionName">Object:</span><span class="Keyword">:</span>toString
<span class="line-numbers"> 45 </span> <span class="FunctionName">hasOwnProperty</span><span class="Keyword">:</span> <span class="FunctionName">Object:</span><span class="Keyword">:</span>hasOwnProperty
<span class="line-numbers"> 46 </span> <span class="FunctionName">propertyIsEnumerable</span><span class="Keyword">:</span> <span class="FunctionName">Object:</span><span class="Keyword">:</span>propertyIsEnumerable
<span class="line-numbers"> 47 </span>
<span class="line-numbers"> 48 </span>
<span class="line-numbers"> 49 </span> <span class="Comment"><span class="Comment">#</span> Current version.</span>
<span class="line-numbers"> 50 </span> <span class="FunctionName">_.VERSION</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span>0.5.5<span class="String">'</span></span>
<span class="line-numbers"> 50 </span> <span class="FunctionName">_.VERSION</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span>0.5.7<span class="String">'</span></span>
<span class="line-numbers"> 51 </span>
<span class="line-numbers"> 52 </span>
<span class="line-numbers"> 53 </span> <span class="Comment"><span class="Comment">#</span> ------------------------ Collection Functions: ---------------------------</span>
<span class="line-numbers"> 54 </span>
<span class="line-numbers"> 55 </span> <span class="Comment"><span class="Comment">#</span> The cornerstone, an each implementation.</span>
<span class="line-numbers"> 56 </span> <span class="Comment"><span class="Comment">#</span> Handles objects implementing forEach, arrays, and raw objects.</span>
<span class="line-numbers"> 57 </span> <span class="FunctionArgument"> _.each: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 57 </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"> 58 </span> <span class="FunctionName">index</span><span class="Keyword">:</span> <span class="Number">0</span>
<span class="line-numbers"> 59 </span> <span class="Keyword">try</span>
<span class="line-numbers"> 60 </span> <span class="Keyword">return</span> obj.forEach(iterator, context) <span class="Keyword">if</span> obj.forEach
@@ -87,36 +87,36 @@
<span class="line-numbers"> 68 </span>
<span class="line-numbers"> 69 </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"> 70 </span> <span class="Comment"><span class="Comment">#</span> 1.6's version of map, if possible.</span>
<span class="line-numbers"> 71 </span> <span class="FunctionArgument"> _.map: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 71 </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"> 72 </span> <span class="Keyword">return</span> obj.map(iterator, context) <span class="Keyword">if</span> (obj <span class="Keyword">and</span> _.isFunction(obj.map))
<span class="line-numbers"> 73 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
<span class="line-numbers"> 74 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 74 </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"> 75 </span> results.push(iterator.call(context, value, index, list))
<span class="line-numbers"> 76 </span> results
<span class="line-numbers"> 77 </span>
<span class="line-numbers"> 78 </span>
<span class="line-numbers"> 79 </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"> 80 </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"> 81 </span> <span class="FunctionArgument"> _.reduce: obj, memo, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 81 </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"> 82 </span> <span class="Keyword">return</span> obj.reduce(_.bind(iterator, context), memo) <span class="Keyword">if</span> (obj <span class="Keyword">and</span> _.isFunction(obj.reduce))
<span class="line-numbers"> 83 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 83 </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"> 84 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> iterator.call(context, memo, value, index, list)
<span class="line-numbers"> 85 </span> memo
<span class="line-numbers"> 86 </span>
<span class="line-numbers"> 87 </span>
<span class="line-numbers"> 88 </span> <span class="Comment"><span class="Comment">#</span> The right-associative version of reduce, also known as foldr. Uses</span>
<span class="line-numbers"> 89 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.8's version of reduceRight, if available.</span>
<span class="line-numbers"> 90 </span> <span class="FunctionArgument"> _.reduceRight: obj, memo, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 90 </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"> 91 </span> <span class="Keyword">return</span> obj.reduceRight(_.bind(iterator, context), memo) <span class="Keyword">if</span> (obj <span class="Keyword">and</span> _.isFunction(obj.reduceRight))
<span class="line-numbers"> 92 </span> _.each(_.clone(_.toArray(obj)).reverse())<span class="FunctionArgument"> value, index </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 92 </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"> 93 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> iterator.call(context, memo, value, index, obj)
<span class="line-numbers"> 94 </span> memo
<span class="line-numbers"> 95 </span>
<span class="line-numbers"> 96 </span>
<span class="line-numbers"> 97 </span> <span class="Comment"><span class="Comment">#</span> Return the first value which passes a truth test.</span>
<span class="line-numbers"> 98 </span> <span class="FunctionArgument"> _.detect: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 98 </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"> 99 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">null</span>
<span class="line-numbers"> 100 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 100 </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"> 101 </span> <span class="Keyword">if</span> iterator.call(context, value, index, list)
<span class="line-numbers"> 102 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> value
<span class="line-numbers"> 103 </span> _.breakLoop()
@@ -125,47 +125,47 @@
<span class="line-numbers"> 106 </span>
<span class="line-numbers"> 107 </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"> 108 </span> <span class="Comment"><span class="Comment">#</span> filter(), if it exists.</span>
<span class="line-numbers"> 109 </span> <span class="FunctionArgument"> _.select: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 109 </span> <span class="FunctionName">_.select</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"> 110 </span> <span class="Keyword">if</span> obj <span class="Keyword">and</span> _.isFunction(obj.filter) <span class="Keyword">then</span> <span class="Keyword">return</span> obj.filter(iterator, context)
<span class="line-numbers"> 111 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
<span class="line-numbers"> 112 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 112 </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"> 113 </span> results.push(value) <span class="Keyword">if</span> iterator.call(context, value, index, list)
<span class="line-numbers"> 114 </span> results
<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 all the elements for which a truth test fails.</span>
<span class="line-numbers"> 118 </span> <span class="FunctionArgument"> _.reject: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 118 </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"> 119 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
<span class="line-numbers"> 120 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</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> results.push(value) <span class="Keyword">if</span> <span class="Keyword">not</span> iterator.call(context, value, index, list)
<span class="line-numbers"> 122 </span> results
<span class="line-numbers"> 123 </span>
<span class="line-numbers"> 124 </span>
<span class="line-numbers"> 125 </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"> 126 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.6's every(), if it is present.</span>
<span class="line-numbers"> 127 </span> <span class="FunctionArgument"> _.all: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 127 </span> <span class="FunctionName">_.all</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"> 128 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
<span class="line-numbers"> 129 </span> <span class="Keyword">return</span> obj.every(iterator, context) <span class="Keyword">if</span> obj <span class="Keyword">and</span> _.isFunction(obj.every)
<span class="line-numbers"> 130 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 131 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 131 </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"> 132 </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"> 133 </span> result
<span class="line-numbers"> 134 </span>
<span class="line-numbers"> 135 </span>
<span class="line-numbers"> 136 </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"> 137 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.6's some(), if it exists.</span>
<span class="line-numbers"> 138 </span> <span class="FunctionArgument"> _.any: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 138 </span> <span class="FunctionName">_.any</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> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
<span class="line-numbers"> 140 </span> <span class="Keyword">return</span> obj.some(iterator, context) <span class="Keyword">if</span> obj <span class="Keyword">and</span> _.isFunction(obj.some)
<span class="line-numbers"> 141 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">false</span>
<span class="line-numbers"> 142 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 142 </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"> 143 </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"> 144 </span> result
<span class="line-numbers"> 145 </span>
<span class="line-numbers"> 146 </span>
<span class="line-numbers"> 147 </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"> 148 </span> <span class="Comment"><span class="Comment">#</span> based on '==='.</span>
<span class="line-numbers"> 149 </span> <span class="FunctionArgument"> _.include: obj, target </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 149 </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"> 150 </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> _.isArray(obj)
<span class="line-numbers"> 151 </span> <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
<span class="line-numbers"> 152 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> val <span class="Keyword">is</span> target
@@ -173,49 +173,49 @@
<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> Invoke a method with arguments on every item in a collection.</span>
<span class="line-numbers"> 157 </span> <span class="FunctionArgument"> _.invoke: obj, method </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 157 </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"> 158 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
<span class="line-numbers"> 159 </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"> 160 </span>
<span class="line-numbers"> 161 </span>
<span class="line-numbers"> 162 </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"> 163 </span> <span class="FunctionArgument"> _.pluck: obj, key </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 164 </span> _.map(obj, (<span class="FunctionArgument">val </span><span class="Storage">=&gt;</span> val[key]))
<span class="line-numbers"> 163 </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"> 164 </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"> 165 </span>
<span class="line-numbers"> 166 </span>
<span class="line-numbers"> 167 </span> <span class="Comment"><span class="Comment">#</span> Return the maximum item or (item-based computation).</span>
<span class="line-numbers"> 168 </span> <span class="FunctionArgument"> _.max: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 168 </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"> 169 </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"> 170 </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"> 171 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 171 </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"> 172 </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"> 173 </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"> 174 </span> result.value
<span class="line-numbers"> 175 </span>
<span class="line-numbers"> 176 </span>
<span class="line-numbers"> 177 </span> <span class="Comment"><span class="Comment">#</span> Return the minimum element (or element-based computation).</span>
<span class="line-numbers"> 178 </span> <span class="FunctionArgument"> _.min: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 178 </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"> 179 </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"> 180 </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"> 181 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 181 </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"> 182 </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"> 183 </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"> 184 </span> result.value
<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> Sort the object's values by a criteria produced by an iterator.</span>
<span class="line-numbers"> 188 </span> <span class="FunctionArgument"> _.sortBy: obj, iterator, context </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 189 </span> _.pluck(((_.map(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 188 </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"> 189 </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"> 190 </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"> 191 </span> ).sort()<span class="FunctionArgument"> left, right </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 191 </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"> 192 </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"> 193 </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"> 194 </span> ), <span class="String"><span class="String">'</span>value<span class="String">'</span></span>)
<span class="line-numbers"> 194 </span> )), <span class="String"><span class="String">'</span>value<span class="String">'</span></span>)
<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> Use a comparator function to figure out at what index an object should</span>
<span class="line-numbers"> 198 </span> <span class="Comment"><span class="Comment">#</span> be inserted so as to maintain order. Uses binary search.</span>
<span class="line-numbers"> 199 </span> <span class="FunctionArgument"> _.sortedIndex: array, obj, iterator </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 199 </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"> 200 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
<span class="line-numbers"> 201 </span> <span class="FunctionName">low</span><span class="Keyword">:</span> <span class="Number">0</span>; <span class="FunctionName">high</span><span class="Keyword">:</span> array.length
<span class="line-numbers"> 202 </span> <span class="Keyword">while</span> low <span class="Keyword">&lt;</span> high
@@ -225,7 +225,7 @@
<span class="line-numbers"> 206 </span>
<span class="line-numbers"> 207 </span>
<span class="line-numbers"> 208 </span> <span class="Comment"><span class="Comment">#</span> Convert anything iterable into a real, live array.</span>
<span class="line-numbers"> 209 </span> <span class="FunctionArgument"> _.toArray: iterable </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 209 </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"> 210 </span> <span class="Keyword">return</span> [] <span class="Keyword">if</span> (<span class="Keyword">!</span>iterable)
<span class="line-numbers"> 211 </span> <span class="Keyword">return</span> iterable.toArray() <span class="Keyword">if</span> (iterable.toArray)
<span class="line-numbers"> 212 </span> <span class="Keyword">return</span> iterable <span class="Keyword">if</span> (_.isArray(iterable))
@@ -234,7 +234,7 @@
<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> Return the number of elements in an object.</span>
<span class="line-numbers"> 218 </span> <span class="FunctionArgument"> _.size: obj </span><span class="Storage">=&gt;</span> _.toArray(obj).length
<span class="line-numbers"> 218 </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"> 219 </span>
<span class="line-numbers"> 220 </span>
<span class="line-numbers"> 221 </span> <span class="Comment"><span class="Comment">#</span> -------------------------- Array Functions: ------------------------------</span>
@@ -242,7 +242,7 @@
<span class="line-numbers"> 223 </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"> 224 </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"> 225 </span> <span class="Comment"><span class="Comment">#</span> with _.map.</span>
<span class="line-numbers"> 226 </span> <span class="FunctionArgument"> _.first: array, n, guard </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 226 </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"> 227 </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"> 228 </span>
<span class="line-numbers"> 229 </span>
@@ -250,35 +250,35 @@
<span class="line-numbers"> 231 </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"> 232 </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"> 233 </span> <span class="Comment"><span class="Comment">#</span> check allows it to work with _.map.</span>
<span class="line-numbers"> 234 </span> <span class="FunctionArgument"> _.rest: array, index, guard </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 234 </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"> 235 </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"> 236 </span>
<span class="line-numbers"> 237 </span>
<span class="line-numbers"> 238 </span> <span class="Comment"><span class="Comment">#</span> Get the last element of an array.</span>
<span class="line-numbers"> 239 </span> <span class="FunctionArgument"> _.last: array </span><span class="Storage">=&gt;</span> array[array.length <span class="Keyword">-</span> <span class="Number">1</span>]
<span class="line-numbers"> 239 </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"> 240 </span>
<span class="line-numbers"> 241 </span>
<span class="line-numbers"> 242 </span> <span class="Comment"><span class="Comment">#</span> Trim out all falsy values from an array.</span>
<span class="line-numbers"> 243 </span> <span class="FunctionArgument"> _.compact: array </span><span class="Storage">=&gt;</span> array[i] <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...array.length] <span class="Keyword">when</span> array[i]
<span class="line-numbers"> 243 </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> array[i] <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...array.length] <span class="Keyword">when</span> array[i]
<span class="line-numbers"> 244 </span>
<span class="line-numbers"> 245 </span>
<span class="line-numbers"> 246 </span> <span class="Comment"><span class="Comment">#</span> Return a completely flattened version of an array.</span>
<span class="line-numbers"> 247 </span> <span class="FunctionArgument"> _.flatten: array </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 248 </span> _.reduce(array, [])<span class="FunctionArgument"> memo, value </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 247 </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"> 248 </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"> 249 </span> <span class="Keyword">return</span> memo.concat(_.flatten(value)) <span class="Keyword">if</span> _.isArray(value)
<span class="line-numbers"> 250 </span> memo.push(value)
<span class="line-numbers"> 251 </span> memo
<span class="line-numbers"> 252 </span>
<span class="line-numbers"> 253 </span>
<span class="line-numbers"> 254 </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"> 255 </span> <span class="FunctionArgument"> _.without: array </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 255 </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"> 256 </span> <span class="FunctionName">values</span><span class="Keyword">:</span> _.rest(arguments)
<span class="line-numbers"> 257 </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"> 258 </span>
<span class="line-numbers"> 259 </span>
<span class="line-numbers"> 260 </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"> 261 </span> <span class="Comment"><span class="Comment">#</span> been sorted, you have the option of using a faster algorithm.</span>
<span class="line-numbers"> 262 </span> <span class="FunctionArgument"> _.uniq: array, isSorted </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 262 </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"> 263 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> []
<span class="line-numbers"> 264 </span> <span class="Keyword">for</span> el, i <span class="Keyword">in</span> _.toArray(array)
<span class="line-numbers"> 265 </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))
@@ -287,330 +287,339 @@
<span class="line-numbers"> 268 </span>
<span class="line-numbers"> 269 </span> <span class="Comment"><span class="Comment">#</span> Produce an array that contains every item shared between all the</span>
<span class="line-numbers"> 270 </span> <span class="Comment"><span class="Comment">#</span> passed-in arrays.</span>
<span class="line-numbers"> 271 </span> <span class="FunctionArgument"> _.intersect: array </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 271 </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"> 272 </span> <span class="FunctionName">rest</span><span class="Keyword">:</span> _.rest(arguments)
<span class="line-numbers"> 273 </span> _.select(_.uniq(array))<span class="FunctionArgument"> item </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 274 </span> _.all(rest)<span class="FunctionArgument"> other </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 273 </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"> 274 </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"> 275 </span> _.indexOf(other, item) <span class="Keyword">&gt;=</span> <span class="Number">0</span>
<span class="line-numbers"> 276 </span>
<span class="line-numbers"> 277 </span>
<span class="line-numbers"> 278 </span> <span class="Comment"><span class="Comment">#</span> Zip together multiple lists into a single array -- elements that share</span>
<span class="line-numbers"> 279 </span> <span class="Comment"><span class="Comment">#</span> an index go together.</span>
<span class="line-numbers"> 280 </span> <span class="FunctionArgument"> _.zip: </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 281 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.toArray(arguments)
<span class="line-numbers"> 282 </span> <span class="FunctionName">length</span><span class="Keyword">:</span> _.max(_.pluck(args, <span class="String"><span class="String">'</span>length<span class="String">'</span></span>))
<span class="line-numbers"> 283 </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"> 284 </span> <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...length]
<span class="line-numbers"> 285 </span> results[i]<span class="Keyword">:</span> _.pluck(args, String(i))
<span class="line-numbers"> 286 </span> results
<span class="line-numbers"> 280 </span> <span class="FunctionName">_.zip</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 281 </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"> 282 </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"> 283 </span> <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...length]
<span class="line-numbers"> 284 </span> results[i]<span class="Keyword">:</span> _.pluck(arguments, String(i))
<span class="line-numbers"> 285 </span> results
<span class="line-numbers"> 286 </span>
<span class="line-numbers"> 287 </span>
<span class="line-numbers"> 288 </span>
<span class="line-numbers"> 289 </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"> 290 </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"> 291 </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"> 292 </span> <span class="FunctionArgument"> _.indexOf: array, item </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 293 </span> <span class="Keyword">return</span> array.indexOf(item) <span class="Keyword">if</span> array.indexOf
<span class="line-numbers"> 294 </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"> 295 </span> <span class="Keyword">while</span> l <span class="Keyword">-</span> i
<span class="line-numbers"> 296 </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"> 297 </span> <span class="Keyword">-</span><span class="Number">1</span>
<span class="line-numbers"> 288 </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"> 289 </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"> 290 </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"> 291 </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"> 292 </span> <span class="Keyword">return</span> array.indexOf(item) <span class="Keyword">if</span> array.indexOf
<span class="line-numbers"> 293 </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"> 294 </span> <span class="Keyword">while</span> l <span class="Keyword">-</span> i
<span class="line-numbers"> 295 </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"> 296 </span> <span class="Keyword">-</span><span class="Number">1</span>
<span class="line-numbers"> 297 </span>
<span class="line-numbers"> 298 </span>
<span class="line-numbers"> 299 </span>
<span class="line-numbers"> 300 </span> <span class="Comment"><span class="Comment">#</span> Provide JavaScript 1.6's lastIndexOf, delegating to the native function,</span>
<span class="line-numbers"> 301 </span> <span class="Comment"><span class="Comment">#</span> if possible.</span>
<span class="line-numbers"> 302 </span> <span class="FunctionArgument"> _.lastIndexOf: array, item </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 303 </span> <span class="Keyword">return</span> array.lastIndexOf(item) <span class="Keyword">if</span> array.lastIndexOf
<span class="line-numbers"> 304 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> array.length
<span class="line-numbers"> 305 </span> <span class="Keyword">while</span> i
<span class="line-numbers"> 306 </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"> 307 </span> <span class="Keyword">-</span><span class="Number">1</span>
<span class="line-numbers"> 299 </span> <span class="Comment"><span class="Comment">#</span> Provide JavaScript 1.6's lastIndexOf, delegating to the native function,</span>
<span class="line-numbers"> 300 </span> <span class="Comment"><span class="Comment">#</span> if possible.</span>
<span class="line-numbers"> 301 </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"> 302 </span> <span class="Keyword">return</span> array.lastIndexOf(item) <span class="Keyword">if</span> array.lastIndexOf
<span class="line-numbers"> 303 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> array.length
<span class="line-numbers"> 304 </span> <span class="Keyword">while</span> i
<span class="line-numbers"> 305 </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"> 306 </span> <span class="Keyword">-</span><span class="Number">1</span>
<span class="line-numbers"> 307 </span>
<span class="line-numbers"> 308 </span>
<span class="line-numbers"> 309 </span>
<span class="line-numbers"> 310 </span> <span class="Comment"><span class="Comment">#</span> Generate an integer Array containing an arithmetic progression. A port of</span>
<span class="line-numbers"> 311 </span> <span class="Comment"><span class="Comment">#</span> the native Python range() function. See:</span>
<span class="line-numbers"> 312 </span> <span class="Comment"><span class="Comment">#</span> http://docs.python.org/library/functions.html#range</span>
<span class="line-numbers"> 313 </span> <span class="FunctionArgument"> _.range: start, stop, step </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 314 </span> <span class="FunctionName">a</span><span class="Keyword">:</span> _.toArray(arguments)
<span class="line-numbers"> 315 </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"> 316 </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"> 317 </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"> 318 </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"> 319 </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"> 320 </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"> 321 </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"> 322 </span> <span class="FunctionName">idx</span><span class="Keyword">:</span> <span class="Number">0</span>
<span class="line-numbers"> 323 </span> <span class="Keyword">while</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 324 </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"> 325 </span> range[idx]<span class="Keyword">:</span> i
<span class="line-numbers"> 326 </span> idx<span class="Keyword">++</span>
<span class="line-numbers"> 327 </span> i<span class="Keyword">+</span><span class="Keyword">=</span> step
<span class="line-numbers"> 309 </span> <span class="Comment"><span class="Comment">#</span> Generate an integer Array containing an arithmetic progression. A port of</span>
<span class="line-numbers"> 310 </span> <span class="Comment"><span class="Comment">#</span> the native Python range() function. See:</span>
<span class="line-numbers"> 311 </span> <span class="Comment"><span class="Comment">#</span> http://docs.python.org/library/functions.html#range</span>
<span class="line-numbers"> 312 </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"> 313 </span> <span class="FunctionName">a</span><span class="Keyword">:</span> arguments
<span class="line-numbers"> 314 </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"> 315 </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"> 316 </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"> 317 </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"> 318 </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"> 319 </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"> 320 </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"> 321 </span> <span class="FunctionName">idx</span><span class="Keyword">:</span> <span class="Number">0</span>
<span class="line-numbers"> 322 </span> <span class="Keyword">while</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 323 </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"> 324 </span> range[idx]<span class="Keyword">:</span> i
<span class="line-numbers"> 325 </span> idx<span class="Keyword">++</span>
<span class="line-numbers"> 326 </span> i<span class="Keyword">+</span><span class="Keyword">=</span> step
<span class="line-numbers"> 327 </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> ----------------------- Function Functions: -----------------------------</span>
<span class="line-numbers"> 331 </span>
<span class="line-numbers"> 332 </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"> 333 </span> <span class="Comment"><span class="Comment">#</span> optionally). Binding with arguments is also known as 'curry'.</span>
<span class="line-numbers"> 334 </span> <span class="FunctionArgument"> _.bind: func, obj </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 335 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
<span class="line-numbers"> 336 </span> <span class="FunctionArgument"> </span><span class="Storage">=&gt;</span> func.apply(obj <span class="Keyword">or</span> root, args.concat(_.toArray(arguments)))
<span class="line-numbers"> 329 </span> <span class="Comment"><span class="Comment">#</span> ----------------------- Function Functions: -----------------------------</span>
<span class="line-numbers"> 330 </span>
<span class="line-numbers"> 331 </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"> 332 </span> <span class="Comment"><span class="Comment">#</span> optionally). Binding with arguments is also known as 'curry'.</span>
<span class="line-numbers"> 333 </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"> 334 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
<span class="line-numbers"> 335 </span> <span class="Storage">-&gt;</span> func.apply(obj <span class="Keyword">or</span> root, args.concat(arguments))
<span class="line-numbers"> 336 </span>
<span class="line-numbers"> 337 </span>
<span class="line-numbers"> 338 </span>
<span class="line-numbers"> 339 </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"> 340 </span> <span class="Comment"><span class="Comment">#</span> all callbacks defined on an object belong to it.</span>
<span class="line-numbers"> 341 </span> <span class="FunctionArgument"> _.bindAll: obj </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 342 </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"> 343 </span> _.each(funcs, (<span class="FunctionArgument">f </span><span class="Storage">=&gt;</span> obj[f]<span class="Keyword">:</span> _.bind(obj[f], obj)))
<span class="line-numbers"> 344 </span> obj
<span class="line-numbers"> 338 </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"> 339 </span> <span class="Comment"><span class="Comment">#</span> all callbacks defined on an object belong to it.</span>
<span class="line-numbers"> 340 </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"> 341 </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"> 342 </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"> 343 </span> obj
<span class="line-numbers"> 344 </span>
<span class="line-numbers"> 345 </span>
<span class="line-numbers"> 346 </span>
<span class="line-numbers"> 347 </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"> 348 </span> <span class="Comment"><span class="Comment">#</span> it with the arguments supplied.</span>
<span class="line-numbers"> 349 </span> <span class="FunctionArgument"> _.delay: func, wait </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 350 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
<span class="line-numbers"> 351 </span> setTimeout((<span class="Storage">=&gt;</span> func.apply(func, args)), wait)
<span class="line-numbers"> 346 </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"> 347 </span> <span class="Comment"><span class="Comment">#</span> it with the arguments supplied.</span>
<span class="line-numbers"> 348 </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"> 349 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
<span class="line-numbers"> 350 </span> setTimeout((<span class="Storage">-&gt;</span> func.apply(func, args)), wait)
<span class="line-numbers"> 351 </span>
<span class="line-numbers"> 352 </span>
<span class="line-numbers"> 353 </span>
<span class="line-numbers"> 354 </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"> 355 </span> <span class="Comment"><span class="Comment">#</span> cleared.</span>
<span class="line-numbers"> 356 </span> <span class="FunctionArgument"> _.defer: func </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 357 </span> _.delay.apply(_, [func, <span class="Number">1</span>].concat(_.rest(arguments)))
<span class="line-numbers"> 353 </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"> 354 </span> <span class="Comment"><span class="Comment">#</span> cleared.</span>
<span class="line-numbers"> 355 </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"> 356 </span> _.delay.apply(_, [func, <span class="Number">1</span>].concat(_.rest(arguments)))
<span class="line-numbers"> 357 </span>
<span class="line-numbers"> 358 </span>
<span class="line-numbers"> 359 </span>
<span class="line-numbers"> 360 </span> <span class="Comment"><span class="Comment">#</span> Returns the first function passed as an argument to the second,</span>
<span class="line-numbers"> 361 </span> <span class="Comment"><span class="Comment">#</span> allowing you to adjust arguments, run code before and after, and</span>
<span class="line-numbers"> 362 </span> <span class="Comment"><span class="Comment">#</span> conditionally execute the original function.</span>
<span class="line-numbers"> 363 </span> <span class="FunctionArgument"> _.wrap: func, wrapper </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 364 </span> <span class="FunctionArgument"> </span><span class="Storage">=&gt;</span> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))
<span class="line-numbers"> 359 </span> <span class="Comment"><span class="Comment">#</span> Returns the first function passed as an argument to the second,</span>
<span class="line-numbers"> 360 </span> <span class="Comment"><span class="Comment">#</span> allowing you to adjust arguments, run code before and after, and</span>
<span class="line-numbers"> 361 </span> <span class="Comment"><span class="Comment">#</span> conditionally execute the original function.</span>
<span class="line-numbers"> 362 </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"> 363 </span> <span class="Storage">-&gt;</span> wrapper.apply(wrapper, [func].concat(arguments))
<span class="line-numbers"> 364 </span>
<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> Returns a function that is the composition of a list of functions, each</span>
<span class="line-numbers"> 368 </span> <span class="Comment"><span class="Comment">#</span> consuming the return value of the function that follows.</span>
<span class="line-numbers"> 369 </span> <span class="FunctionArgument"> _.compose: </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 370 </span> <span class="FunctionName">funcs</span><span class="Keyword">:</span> _.toArray(arguments)
<span class="line-numbers"> 371 </span> <span class="FunctionArgument"> </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 372 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.toArray(arguments)
<span class="line-numbers"> 373 </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"> 374 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> [funcs[i].apply(<span class="Variable">this</span>, args)]
<span class="line-numbers"> 375 </span> args[<span class="Number">0</span>]
<span class="line-numbers"> 366 </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"> 367 </span> <span class="Comment"><span class="Comment">#</span> consuming the return value of the function that follows.</span>
<span class="line-numbers"> 368 </span> <span class="FunctionName">_.compose</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 369 </span> <span class="FunctionName">funcs</span><span class="Keyword">:</span> arguments
<span class="line-numbers"> 370 </span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 371 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> arguments
<span class="line-numbers"> 372 </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"> 373 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> [funcs[i].apply(<span class="Variable">this</span>, args)]
<span class="line-numbers"> 374 </span> args[<span class="Number">0</span>]
<span class="line-numbers"> 375 </span>
<span class="line-numbers"> 376 </span>
<span class="line-numbers"> 377 </span>
<span class="line-numbers"> 378 </span> <span class="Comment"><span class="Comment">#</span> ------------------------- Object Functions: ----------------------------</span>
<span class="line-numbers"> 379 </span>
<span class="line-numbers"> 380 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the names of an object's properties.</span>
<span class="line-numbers"> 381 </span> <span class="FunctionArgument"> _.keys: obj </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 382 </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"> 383 </span> key <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
<span class="line-numbers"> 377 </span> <span class="Comment"><span class="Comment">#</span> ------------------------- Object Functions: ----------------------------</span>
<span class="line-numbers"> 378 </span>
<span class="line-numbers"> 379 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the names of an object's properties.</span>
<span class="line-numbers"> 380 </span> <span class="FunctionName">_.keys</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"> 381 </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"> 382 </span> key <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
<span class="line-numbers"> 383 </span>
<span class="line-numbers"> 384 </span>
<span class="line-numbers"> 385 </span>
<span class="line-numbers"> 386 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the values of an object's properties.</span>
<span class="line-numbers"> 387 </span> <span class="FunctionArgument"> _.values: obj </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 388 </span> _.map(obj, _.identity)
<span class="line-numbers"> 385 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the values of an object's properties.</span>
<span class="line-numbers"> 386 </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"> 387 </span> _.map(obj, _.identity)
<span class="line-numbers"> 388 </span>
<span class="line-numbers"> 389 </span>
<span class="line-numbers"> 390 </span>
<span class="line-numbers"> 391 </span> <span class="Comment"><span class="Comment">#</span> Return a sorted list of the function names available in Underscore.</span>
<span class="line-numbers"> 392 </span> <span class="FunctionArgument"> _.functions: obj </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 393 </span> _.select(_.keys(obj)<span class="FunctionArgument">, key </span><span class="Storage">=&gt;</span> _.isFunction(obj[key])).sort()
<span class="line-numbers"> 390 </span> <span class="Comment"><span class="Comment">#</span> Return a sorted list of the function names available in Underscore.</span>
<span class="line-numbers"> 391 </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"> 392 </span> _.select(_.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"> 393 </span>
<span class="line-numbers"> 394 </span>
<span class="line-numbers"> 395 </span>
<span class="line-numbers"> 396 </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"> 397 </span> <span class="FunctionArgument"> _.extend: destination, source </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 398 </span> <span class="Keyword">for</span> key, val <span class="Keyword">of</span> source
<span class="line-numbers"> 399 </span> destination[key]<span class="Keyword">:</span> val
<span class="line-numbers"> 400 </span> destination
<span class="line-numbers"> 395 </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"> 396 </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"> 397 </span> <span class="Keyword">for</span> key, val <span class="Keyword">of</span> source
<span class="line-numbers"> 398 </span> destination[key]<span class="Keyword">:</span> val
<span class="line-numbers"> 399 </span> destination
<span class="line-numbers"> 400 </span>
<span class="line-numbers"> 401 </span>
<span class="line-numbers"> 402 </span>
<span class="line-numbers"> 403 </span> <span class="Comment"><span class="Comment">#</span> Create a (shallow-cloned) duplicate of an object.</span>
<span class="line-numbers"> 404 </span> <span class="FunctionArgument"> _.clone: obj </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 405 </span> <span class="Keyword">return</span> obj.slice(<span class="Number">0</span>) <span class="Keyword">if</span> _.isArray(obj)
<span class="line-numbers"> 406 </span> _.extend({}, obj)
<span class="line-numbers"> 402 </span> <span class="Comment"><span class="Comment">#</span> Create a (shallow-cloned) duplicate of an object.</span>
<span class="line-numbers"> 403 </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"> 404 </span> <span class="Keyword">return</span> obj.slice(<span class="Number">0</span>) <span class="Keyword">if</span> _.isArray(obj)
<span class="line-numbers"> 405 </span> _.extend({}, obj)
<span class="line-numbers"> 406 </span>
<span class="line-numbers"> 407 </span>
<span class="line-numbers"> 408 </span>
<span class="line-numbers"> 409 </span> <span class="Comment"><span class="Comment">#</span> Invokes interceptor with the obj, and then returns obj.</span>
<span class="line-numbers"> 410 </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"> 411 </span> <span class="FunctionArgument"> _.tap: obj, interceptor </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 412 </span> interceptor(obj)
<span class="line-numbers"> 413 </span> obj
<span class="line-numbers"> 408 </span> <span class="Comment"><span class="Comment">#</span> Invokes interceptor with the obj, and then returns obj.</span>
<span class="line-numbers"> 409 </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"> 410 </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"> 411 </span> interceptor(obj)
<span class="line-numbers"> 412 </span> obj
<span class="line-numbers"> 413 </span>
<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> Perform a deep comparison to check if two objects are equal.</span>
<span class="line-numbers"> 417 </span> <span class="FunctionArgument"> _.isEqual: a, b </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 418 </span> <span class="Comment"><span class="Comment">#</span> Check object identity.</span>
<span class="line-numbers"> 419 </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"> 420 </span> <span class="Comment"><span class="Comment">#</span> Different types?</span>
<span class="line-numbers"> 421 </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"> 422 </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"> 423 </span> <span class="Comment"><span class="Comment">#</span> Basic equality test (watch out for coercions).</span>
<span class="line-numbers"> 424 </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"> 425 </span> <span class="Comment"><span class="Comment">#</span> One is falsy and the other truthy.</span>
<span class="line-numbers"> 426 </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"> 427 </span> <span class="Comment"><span class="Comment">#</span> One of them implements an isEqual()?</span>
<span class="line-numbers"> 428 </span> <span class="Keyword">return</span> a.isEqual(b) <span class="Keyword">if</span> a.isEqual
<span class="line-numbers"> 429 </span> <span class="Comment"><span class="Comment">#</span> Check dates' integer values.</span>
<span class="line-numbers"> 430 </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"> 431 </span> <span class="Comment"><span class="Comment">#</span> Both are NaN?</span>
<span class="line-numbers"> 432 </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"> 433 </span> <span class="Comment"><span class="Comment">#</span> Compare regular expressions.</span>
<span class="line-numbers"> 434 </span> <span class="Keyword">if</span> _.isRegExp(a) <span class="Keyword">and</span> _.isRegExp(b)
<span class="line-numbers"> 435 </span> <span class="Keyword">return</span> a.source <span class="Keyword">is</span> b.source <span class="Keyword">and</span>
<span class="line-numbers"> 436 </span> a.global <span class="Keyword">is</span> b.global <span class="Keyword">and</span>
<span class="line-numbers"> 437 </span> a.ignoreCase <span class="Keyword">is</span> b.ignoreCase <span class="Keyword">and</span>
<span class="line-numbers"> 438 </span> a.multiline <span class="Keyword">is</span> b.multiline
<span class="line-numbers"> 439 </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"> 440 </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"> 441 </span> <span class="Comment"><span class="Comment">#</span> Check for different array lengths before comparing contents.</span>
<span class="line-numbers"> 442 </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"> 443 </span> <span class="Comment"><span class="Comment">#</span> Nothing else worked, deep compare the contents.</span>
<span class="line-numbers"> 444 </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"> 445 </span> <span class="Comment"><span class="Comment">#</span> Different object sizes?</span>
<span class="line-numbers"> 446 </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"> 447 </span> <span class="Comment"><span class="Comment">#</span> Recursive comparison of contents.</span>
<span class="line-numbers"> 448 </span> <span class="Comment"><span class="Comment">#</span> for (var key in a) if (!_.isEqual(a[key], b[key])) return false;</span>
<span class="line-numbers"> 449 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 415 </span> <span class="Comment"><span class="Comment">#</span> Perform a deep comparison to check if two objects are equal.</span>
<span class="line-numbers"> 416 </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"> 417 </span> <span class="Comment"><span class="Comment">#</span> Check object identity.</span>
<span class="line-numbers"> 418 </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"> 419 </span> <span class="Comment"><span class="Comment">#</span> Different types?</span>
<span class="line-numbers"> 420 </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"> 421 </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"> 422 </span> <span class="Comment"><span class="Comment">#</span> Basic equality test (watch out for coercions).</span>
<span class="line-numbers"> 423 </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"> 424 </span> <span class="Comment"><span class="Comment">#</span> One is falsy and the other truthy.</span>
<span class="line-numbers"> 425 </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"> 426 </span> <span class="Comment"><span class="Comment">#</span> One of them implements an isEqual()?</span>
<span class="line-numbers"> 427 </span> <span class="Keyword">return</span> a.isEqual(b) <span class="Keyword">if</span> a.isEqual
<span class="line-numbers"> 428 </span> <span class="Comment"><span class="Comment">#</span> Check dates' integer values.</span>
<span class="line-numbers"> 429 </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"> 430 </span> <span class="Comment"><span class="Comment">#</span> Both are NaN?</span>
<span class="line-numbers"> 431 </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"> 432 </span> <span class="Comment"><span class="Comment">#</span> Compare regular expressions.</span>
<span class="line-numbers"> 433 </span> <span class="Keyword">if</span> _.isRegExp(a) <span class="Keyword">and</span> _.isRegExp(b)
<span class="line-numbers"> 434 </span> <span class="Keyword">return</span> a.source <span class="Keyword">is</span> b.source <span class="Keyword">and</span>
<span class="line-numbers"> 435 </span> a.global <span class="Keyword">is</span> b.global <span class="Keyword">and</span>
<span class="line-numbers"> 436 </span> a.ignoreCase <span class="Keyword">is</span> b.ignoreCase <span class="Keyword">and</span>
<span class="line-numbers"> 437 </span> a.multiline <span class="Keyword">is</span> b.multiline
<span class="line-numbers"> 438 </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"> 439 </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"> 440 </span> <span class="Comment"><span class="Comment">#</span> Check for different array lengths before comparing contents.</span>
<span class="line-numbers"> 441 </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"> 442 </span> <span class="Comment"><span class="Comment">#</span> Nothing else worked, deep compare the contents.</span>
<span class="line-numbers"> 443 </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"> 444 </span> <span class="Comment"><span class="Comment">#</span> Different object sizes?</span>
<span class="line-numbers"> 445 </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"> 446 </span> <span class="Comment"><span class="Comment">#</span> Recursive comparison of contents.</span>
<span class="line-numbers"> 447 </span> <span class="Comment"><span class="Comment">#</span> for (var key in a) if (!_.isEqual(a[key], b[key])) return false;</span>
<span class="line-numbers"> 448 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 449 </span>
<span class="line-numbers"> 450 </span>
<span class="line-numbers"> 451 </span>
<span class="line-numbers"> 452 </span> <span class="Comment"><span class="Comment">#</span> Is a given array or object empty?</span>
<span class="line-numbers"> 453 </span> <span class="FunctionArgument"> _.isEmpty: obj </span><span class="Storage">=&gt;</span> _.keys(obj).length <span class="Keyword">is</span> <span class="Number">0</span>
<span class="line-numbers"> 451 </span> <span class="Comment"><span class="Comment">#</span> Is a given array or object empty?</span>
<span class="line-numbers"> 452 </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> _.keys(obj).length <span class="Keyword">is</span> <span class="Number">0</span>
<span class="line-numbers"> 453 </span>
<span class="line-numbers"> 454 </span>
<span class="line-numbers"> 455 </span>
<span class="line-numbers"> 456 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a DOM element?</span>
<span class="line-numbers"> 457 </span> <span class="FunctionArgument"> _.isElement: obj </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"> 455 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a DOM element?</span>
<span class="line-numbers"> 456 </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"> 457 </span>
<span class="line-numbers"> 458 </span>
<span class="line-numbers"> 459 </span>
<span class="line-numbers"> 460 </span> <span class="Comment"><span class="Comment">#</span> Is a given value an array?</span>
<span class="line-numbers"> 461 </span> <span class="FunctionArgument"> _.isArray: obj </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"> 459 </span> <span class="Comment"><span class="Comment">#</span> Is a given value an array?</span>
<span class="line-numbers"> 460 </span> <span class="FunctionName">_.isArray</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.concat <span class="Keyword">and</span> obj.unshift)
<span class="line-numbers"> 461 </span>
<span class="line-numbers"> 462 </span>
<span class="line-numbers"> 463 </span>
<span class="line-numbers"> 464 </span> <span class="Comment"><span class="Comment">#</span> Is a given variable an arguments object?</span>
<span class="line-numbers"> 465 </span> <span class="FunctionArgument"> _.isArguments: obj </span><span class="Storage">=&gt;</span> obj <span class="Keyword">and</span> _.isNumber(obj.length) <span class="Keyword">and</span> <span class="Keyword">!</span>_.isArray(obj) <span class="Keyword">and</span> <span class="Keyword">!</span>propertyIsEnumerable.call(obj, <span class="String"><span class="String">'</span>length<span class="String">'</span></span>)
<span class="line-numbers"> 463 </span> <span class="Comment"><span class="Comment">#</span> Is a given variable an arguments object?</span>
<span class="line-numbers"> 464 </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"> 465 </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"> 466 </span>
<span class="line-numbers"> 467 </span>
<span class="line-numbers"> 468 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a function?</span>
<span class="line-numbers"> 469 </span> <span class="FunctionArgument"> _.isFunction: obj </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"> 469 </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"> 470 </span>
<span class="line-numbers"> 471 </span>
<span class="line-numbers"> 472 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a string?</span>
<span class="line-numbers"> 473 </span> <span class="FunctionArgument"> _.isString: obj </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"> 473 </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"> 474 </span>
<span class="line-numbers"> 475 </span>
<span class="line-numbers"> 476 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a number?</span>
<span class="line-numbers"> 477 </span> <span class="FunctionArgument"> _.isNumber: obj </span><span class="Storage">=&gt;</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"> 477 </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"> 478 </span>
<span class="line-numbers"> 479 </span>
<span class="line-numbers"> 480 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a Date?</span>
<span class="line-numbers"> 481 </span> <span class="FunctionArgument"> _.isDate: obj </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"> 481 </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"> 482 </span>
<span class="line-numbers"> 483 </span>
<span class="line-numbers"> 484 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a regular expression?</span>
<span class="line-numbers"> 485 </span> <span class="FunctionArgument"> _.isRegExp: obj </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"> 485 </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"> 486 </span>
<span class="line-numbers"> 487 </span>
<span class="line-numbers"> 488 </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"> 489 </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"> 490 </span> <span class="FunctionArgument"> _.isNaN: obj </span><span class="Storage">=&gt;</span> _.isNumber(obj) <span class="Keyword">and</span> window.isNaN(obj)
<span class="line-numbers"> 490 </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"> 491 </span>
<span class="line-numbers"> 492 </span>
<span class="line-numbers"> 493 </span> <span class="Comment"><span class="Comment">#</span> Is a given value equal to null?</span>
<span class="line-numbers"> 494 </span> <span class="FunctionArgument"> _.isNull: obj </span><span class="Storage">=&gt;</span> obj <span class="Keyword">is</span> <span class="BuiltInConstant">null</span>
<span class="line-numbers"> 494 </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"> 495 </span>
<span class="line-numbers"> 496 </span>
<span class="line-numbers"> 497 </span> <span class="Comment"><span class="Comment">#</span> Is a given variable undefined?</span>
<span class="line-numbers"> 498 </span> <span class="FunctionArgument"> _.isUndefined: obj </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"> 498 </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"> 499 </span>
<span class="line-numbers"> 500 </span>
<span class="line-numbers"> 501 </span> <span class="Comment"><span class="Comment">#</span> -------------------------- Utility Functions: --------------------------</span>
<span class="line-numbers"> 502 </span>
<span class="line-numbers"> 503 </span> <span class="Comment"><span class="Comment">#</span> Run Underscore.js in noConflict mode, returning the '_' variable to its</span>
<span class="line-numbers"> 504 </span> <span class="Comment"><span class="Comment">#</span> previous owner. Returns a reference to the Underscore object.</span>
<span class="line-numbers"> 505 </span> <span class="FunctionArgument"> _.noConflict: </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 505 </span> <span class="FunctionName">_.noConflict</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 506 </span> <span class="FunctionName">root._</span><span class="Keyword">:</span> previousUnderscore
<span class="line-numbers"> 507 </span> <span class="Variable">this</span>
<span class="line-numbers"> 508 </span>
<span class="line-numbers"> 509 </span>
<span class="line-numbers"> 510 </span> <span class="Comment"><span class="Comment">#</span> Keep the identity function around for default iterators.</span>
<span class="line-numbers"> 511 </span> <span class="FunctionArgument"> _.identity: value </span><span class="Storage">=&gt;</span> value
<span class="line-numbers"> 511 </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"> 512 </span>
<span class="line-numbers"> 513 </span>
<span class="line-numbers"> 514 </span> <span class="Comment"><span class="Comment">#</span> Break out of the middle of an iteration.</span>
<span class="line-numbers"> 515 </span> <span class="FunctionArgument"> _.breakLoop: </span><span class="Storage">=&gt;</span> <span class="Keyword">throw</span> breaker
<span class="line-numbers"> 515 </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"> 516 </span>
<span class="line-numbers"> 517 </span>
<span class="line-numbers"> 518 </span> <span class="Comment"><span class="Comment">#</span> Generate a unique integer id (unique within the entire client session).</span>
<span class="line-numbers"> 519 </span> <span class="Comment"><span class="Comment">#</span> Useful for temporary DOM ids.</span>
<span class="line-numbers"> 520 </span> <span class="FunctionName">idCounter</span><span class="Keyword">:</span> <span class="Number">0</span>
<span class="line-numbers"> 521 </span> <span class="FunctionArgument"> _.uniqueId: prefix </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 521 </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"> 522 </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"> 523 </span>
<span class="line-numbers"> 524 </span>
<span class="line-numbers"> 525 </span> <span class="Comment"><span class="Comment">#</span> JavaScript templating a-la ERB, pilfered from John Resig's</span>
<span class="line-numbers"> 526 </span> <span class="Comment"><span class="Comment">#</span> &quot;Secrets of the JavaScript Ninja&quot;, page 83.</span>
<span class="line-numbers"> 527 </span> <span class="FunctionArgument"> _.template: str, data </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 528 </span> <span class="String"><span class="String">`</span>var fn = new Function('obj',</span>
<span class="line-numbers"> 529 </span> <span class="String"> 'var p=[],print=function(){p.push.apply(p,arguments);};' +</span>
<span class="line-numbers"> 530 </span> <span class="String"> 'with(obj){p.push(<span class="UserDefinedConstant">\'</span>' +</span>
<span class="line-numbers"> 531 </span> <span class="String"> str.</span>
<span class="line-numbers"> 532 </span> <span class="String"> replace(/[<span class="UserDefinedConstant">\r</span><span class="UserDefinedConstant">\t</span><span class="UserDefinedConstant">\n</span>]/g, &quot; &quot;).</span>
<span class="line-numbers"> 533 </span> <span class="String"> split(&quot;&lt;%&quot;).join(&quot;<span class="UserDefinedConstant">\t</span>&quot;).</span>
<span class="line-numbers"> 534 </span> <span class="String"> replace(/((^|%&gt;)[^<span class="UserDefinedConstant">\t</span>]*)'/g, &quot;$1<span class="UserDefinedConstant">\r</span>&quot;).</span>
<span class="line-numbers"> 535 </span> <span class="String"> replace(/<span class="UserDefinedConstant">\t</span>=(.*?)%&gt;/g, &quot;',$1,'&quot;).</span>
<span class="line-numbers"> 536 </span> <span class="String"> split(&quot;<span class="UserDefinedConstant">\t</span>&quot;).join(&quot;');&quot;).</span>
<span class="line-numbers"> 537 </span> <span class="String"> split(&quot;%&gt;&quot;).join(&quot;p.push('&quot;).</span>
<span class="line-numbers"> 538 </span> <span class="String"> split(&quot;<span class="UserDefinedConstant">\r</span>&quot;).join(&quot;<span class="UserDefinedConstant">\\</span>'&quot;) +</span>
<span class="line-numbers"> 539 </span> <span class="String"> &quot;');}return p.join('');&quot;)<span class="String">`</span></span>
<span class="line-numbers"> 540 </span> <span class="Keyword">if</span> data <span class="Keyword">then</span> fn(data) <span class="Keyword">else</span> fn
<span class="line-numbers"> 541 </span>
<span class="line-numbers"> 542 </span>
<span class="line-numbers"> 543 </span> <span class="Comment"><span class="Comment">#</span> ------------------------------- Aliases ----------------------------------</span>
<span class="line-numbers"> 544 </span>
<span class="line-numbers"> 545 </span> <span class="FunctionName">_.forEach</span><span class="Keyword">:</span> _.each
<span class="line-numbers"> 546 </span> <span class="FunctionName">_.foldl</span><span class="Keyword">:</span> <span class="FunctionName">_.inject</span><span class="Keyword">:</span> _.reduce
<span class="line-numbers"> 547 </span> <span class="FunctionName">_.foldr</span><span class="Keyword">:</span> _.reduceRight
<span class="line-numbers"> 548 </span> <span class="FunctionName">_.filter</span><span class="Keyword">:</span> _.select
<span class="line-numbers"> 549 </span> <span class="FunctionName">_.every</span><span class="Keyword">:</span> _.all
<span class="line-numbers"> 550 </span> <span class="FunctionName">_.some</span><span class="Keyword">:</span> _.any
<span class="line-numbers"> 551 </span> <span class="FunctionName">_.head</span><span class="Keyword">:</span> _.first
<span class="line-numbers"> 552 </span> <span class="FunctionName">_.tail</span><span class="Keyword">:</span> _.rest
<span class="line-numbers"> 553 </span> <span class="FunctionName">_.methods</span><span class="Keyword">:</span> _.functions
<span class="line-numbers"> 525 </span> <span class="Comment"><span class="Comment">#</span> By default, Underscore uses ERB-style template delimiters, change the</span>
<span class="line-numbers"> 526 </span> <span class="Comment"><span class="Comment">#</span> following template settings to use alternative delimiters.</span>
<span class="line-numbers"> 527 </span> <span class="FunctionName">_.templateSettings</span><span class="Keyword">:</span> {
<span class="line-numbers"> 528 </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"> 529 </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"> 530 </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"> 531 </span> }
<span class="line-numbers"> 532 </span>
<span class="line-numbers"> 533 </span>
<span class="line-numbers"> 534 </span> <span class="Comment"><span class="Comment">#</span> JavaScript templating a-la ERB, pilfered from John Resig's</span>
<span class="line-numbers"> 535 </span> <span class="Comment"><span class="Comment">#</span> &quot;Secrets of the JavaScript Ninja&quot;, page 83.</span>
<span class="line-numbers"> 536 </span> <span class="Comment"><span class="Comment">#</span> Single-quotea fix from Rick Strahl's version.</span>
<span class="line-numbers"> 537 </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"> 538 </span> <span class="FunctionName">c</span><span class="Keyword">:</span> _.templateSettings
<span class="line-numbers"> 539 </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"> 540 </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"> 541 </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"> 542 </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"> 543 </span> .replace(<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[<span class="Number">0</span>]<span class="Keyword">+</span><span class="String"><span class="String">&quot;</span>]*<span class="String">&quot;</span></span><span class="Keyword">+</span>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="String"><span class="String">&quot;</span><span class="UserDefinedConstant">\t</span><span class="String">&quot;</span></span>)
<span class="line-numbers"> 544 </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"> 545 </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"> 546 </span> .replace(c.interpolate, <span class="String"><span class="String">&quot;</span>',$1,'<span class="String">&quot;</span></span>)
<span class="line-numbers"> 547 </span> .split(c.start).join(<span class="String"><span class="String">&quot;</span>');<span class="String">&quot;</span></span>)
<span class="line-numbers"> 548 </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"> 549 </span> <span class="String"><span class="String">&quot;</span>');}return p.join('');<span class="String">&quot;</span></span>
<span class="line-numbers"> 550 </span> <span class="Keyword">if</span> data <span class="Keyword">then</span> fn(data) <span class="Keyword">else</span> fn
<span class="line-numbers"> 551 </span>
<span class="line-numbers"> 552 </span>
<span class="line-numbers"> 553 </span> <span class="Comment"><span class="Comment">#</span> ------------------------------- Aliases ----------------------------------</span>
<span class="line-numbers"> 554 </span>
<span class="line-numbers"> 555 </span>
<span class="line-numbers"> 556 </span> <span class="Comment"><span class="Comment">#</span> /*------------------------ Setup the OOP Wrapper: --------------------------*/</span>
<span class="line-numbers"> 557 </span>
<span class="line-numbers"> 558 </span> <span class="Comment"><span class="Comment">#</span> Helper function to continue chaining intermediate results.</span>
<span class="line-numbers"> 559 </span> <span class="FunctionArgument"> result: obj, chain </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 560 </span> <span class="Keyword">if</span> chain <span class="Keyword">then</span> _(obj).chain() <span class="Keyword">else</span> obj
<span class="line-numbers"> 561 </span>
<span class="line-numbers"> 562 </span>
<span class="line-numbers"> 563 </span> <span class="Comment"><span class="Comment">#</span> Add all of the Underscore functions to the wrapper object.</span>
<span class="line-numbers"> 564 </span> _.each(_.functions(_))<span class="FunctionArgument"> name </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 565 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> _[name]
<span class="line-numbers"> 566 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 567 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.toArray(arguments)
<span class="line-numbers"> 568 </span> unshift.call(args, <span class="Variable">this</span>._wrapped)
<span class="line-numbers"> 569 </span> result(method.apply(_, args), <span class="Variable">this</span>._chain)
<span class="line-numbers"> 570 </span>
<span class="line-numbers"> 555 </span> <span class="FunctionName">_.forEach</span><span class="Keyword">:</span> _.each
<span class="line-numbers"> 556 </span> <span class="FunctionName">_.foldl</span><span class="Keyword">:</span> <span class="FunctionName">_.inject</span><span class="Keyword">:</span> _.reduce
<span class="line-numbers"> 557 </span> <span class="FunctionName">_.foldr</span><span class="Keyword">:</span> _.reduceRight
<span class="line-numbers"> 558 </span> <span class="FunctionName">_.filter</span><span class="Keyword">:</span> _.select
<span class="line-numbers"> 559 </span> <span class="FunctionName">_.every</span><span class="Keyword">:</span> _.all
<span class="line-numbers"> 560 </span> <span class="FunctionName">_.some</span><span class="Keyword">:</span> _.any
<span class="line-numbers"> 561 </span> <span class="FunctionName">_.head</span><span class="Keyword">:</span> _.first
<span class="line-numbers"> 562 </span> <span class="FunctionName">_.tail</span><span class="Keyword">:</span> _.rest
<span class="line-numbers"> 563 </span> <span class="FunctionName">_.methods</span><span class="Keyword">:</span> _.functions
<span class="line-numbers"> 564 </span>
<span class="line-numbers"> 565 </span>
<span class="line-numbers"> 566 </span> <span class="Comment"><span class="Comment">#</span> /*------------------------ Setup the OOP Wrapper: --------------------------*/</span>
<span class="line-numbers"> 567 </span>
<span class="line-numbers"> 568 </span> <span class="Comment"><span class="Comment">#</span> Helper function to continue chaining intermediate results.</span>
<span class="line-numbers"> 569 </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"> 570 </span> <span class="Keyword">if</span> chain <span class="Keyword">then</span> _(obj).chain() <span class="Keyword">else</span> obj
<span class="line-numbers"> 571 </span>
<span class="line-numbers"> 572 </span> <span class="Comment"><span class="Comment">#</span> Add all mutator Array functions to the wrapper.</span>
<span class="line-numbers"> 573 </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"> name </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 574 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
<span class="line-numbers"> 575 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 576 </span> method.apply(<span class="Variable">this</span>._wrapped, arguments)
<span class="line-numbers"> 577 </span> result(<span class="Variable">this</span>._wrapped, <span class="Variable">this</span>._chain)
<span class="line-numbers"> 578 </span>
<span class="line-numbers"> 572 </span>
<span class="line-numbers"> 573 </span> <span class="Comment"><span class="Comment">#</span> Add all of the Underscore functions to the wrapper object.</span>
<span class="line-numbers"> 574 </span> _.each _.functions(_), <span class="FunctionArgument">(</span><span class="FunctionArgument">name</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 575 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> _[name]
<span class="line-numbers"> 576 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 577 </span> unshift.call(arguments, <span class="Variable">this</span>._wrapped)
<span class="line-numbers"> 578 </span> result(method.apply(_, arguments), <span class="Variable">this</span>._chain)
<span class="line-numbers"> 579 </span>
<span class="line-numbers"> 580 </span> <span class="Comment"><span class="Comment">#</span> Add all accessor Array functions to the wrapper.</span>
<span class="line-numbers"> 581 </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"> name </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 582 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
<span class="line-numbers"> 583 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 584 </span> result(method.apply(<span class="Variable">this</span>._wrapped, arguments), <span class="Variable">this</span>._chain)
<span class="line-numbers"> 585 </span>
<span class="line-numbers"> 586 </span>
<span class="line-numbers"> 587 </span> <span class="Comment"><span class="Comment">#</span> Start chaining a wrapped Underscore object.</span>
<span class="line-numbers"> 588 </span> <span class="FunctionArgument"> wrapper::chain: </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 589 </span> <span class="FunctionName">this._chain</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 590 </span> <span class="Variable">this</span>
<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> Extracts the result from a wrapped and chained object.</span>
<span class="line-numbers"> 594 </span> <span class="FunctionArgument"> wrapper::value: </span><span class="Storage">=&gt;</span> <span class="Variable">this</span>._wrapped
<span class="line-numbers"> 580 </span>
<span class="line-numbers"> 581 </span> <span class="Comment"><span class="Comment">#</span> Add all mutator Array functions to the wrapper.</span>
<span class="line-numbers"> 582 </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"> 583 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
<span class="line-numbers"> 584 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 585 </span> method.apply(<span class="Variable">this</span>._wrapped, arguments)
<span class="line-numbers"> 586 </span> result(<span class="Variable">this</span>._wrapped, <span class="Variable">this</span>._chain)
<span class="line-numbers"> 587 </span>
<span class="line-numbers"> 588 </span>
<span class="line-numbers"> 589 </span> <span class="Comment"><span class="Comment">#</span> Add all accessor Array functions to the wrapper.</span>
<span class="line-numbers"> 590 </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"> 591 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
<span class="line-numbers"> 592 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 593 </span> result(method.apply(<span class="Variable">this</span>._wrapped, arguments), <span class="Variable">this</span>._chain)
<span class="line-numbers"> 594 </span>
<span class="line-numbers"> 595 </span>
<span class="line-numbers"> 596 </span> <span class="Comment"><span class="Comment">#</span> Start chaining a wrapped Underscore object.</span>
<span class="line-numbers"> 597 </span> <span class="FunctionName">wrapper::chain</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="line-numbers"> 598 </span> <span class="FunctionName">this._chain</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
<span class="line-numbers"> 599 </span> <span class="Variable">this</span>
<span class="line-numbers"> 600 </span>
<span class="line-numbers"> 601 </span>
<span class="line-numbers"> 602 </span> <span class="Comment"><span class="Comment">#</span> Extracts the result from a wrapped and chained object.</span>
<span class="line-numbers"> 603 </span> <span class="FunctionName">wrapper::value</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span> <span class="Variable">this</span>._wrapped
</pre> <p>
<a href="http://validator.w3.org/check?uri=referer">
<img style="border:0"

View File

@@ -0,0 +1,16 @@
# Beautiful Code, Chapter 6.
# The implementation of binary search that is tested.
# Return the index of an element in a sorted list. (or -1, if not present)
index: (list, target) ->
[low, high]: [0, list.length]
while low < high
mid: (low + high) >> 1
val: list[mid]
return mid if val is target
if val < target then low: mid + 1 else high: mid
return -1
print(2 is index([10, 20, 30, 40, 50], 30))
print(4 is index([-97, 35, 67, 88, 1200], 1200))
print(0 is index([0, 45, 70], 0))

View File

@@ -0,0 +1,13 @@
# Beautiful Code, Chapter 3.
# Produces the expected runtime of Quicksort, for every integer from 1 to N.
runtime: (N) ->
[sum, t]: [0, 0]
for n in [1..N]
sum += 2 * t
t: n - 1 + sum / n
t
print(runtime(3) is 2.6666666666666665)
print(runtime(5) is 7.4)
print(runtime(8) is 16.92142857142857)

View File

@@ -0,0 +1,34 @@
# Beautiful Code, Chapter 1.
# Implements a regular expression matcher that supports character matches,
# '.', '^', '$', and '*'.
# Search for the regexp anywhere in the text.
match: (regexp, text) ->
return match_here(regexp.slice(1), text) if regexp[0] is '^'
while text
return true if match_here(regexp, text)
text: text.slice(1)
false
# Search for the regexp at the beginning of the text.
match_here: (regexp, text) ->
[cur, next]: [regexp[0], regexp[1]]
if regexp.length is 0 then return true
if next is '*' then return match_star(cur, regexp.slice(2), text)
if cur is '$' and not next then return text.length is 0
if text and (cur is '.' or cur is text[0]) then return match_here(regexp.slice(1), text.slice(1))
false
# Search for a kleene star match at the beginning of the text.
match_star: (c, regexp, text) ->
while true
return true if match_here(regexp, text)
return false unless text and (text[0] is c or c is '.')
text: text.slice(1)
print(match("ex", "some text"))
print(match("s..t", "spit"))
print(match("^..t", "buttercup"))
print(match("i..$", "cherries"))
print(match("o*m", "vrooooommm!"))
print(match("^hel*o$", "hellllllo"))

View File

@@ -1,14 +1,14 @@
# Functions:
square: x => x * x
square: (x) -> x * x
sum: x, y => x + y
sum: (x, y) -> x + y
odd: x => x % 2 is 0
odd: (x) -> x % 2 isnt 0
even: x => x % 2 isnt 0
even: (x) -> x % 2 is 0
run_loop: =>
fire_events(e => e.stopPropagation())
run_loop: ->
fire_events((e) -> e.stopPropagation())
listen()
wait()
@@ -22,14 +22,14 @@ spaced_out_multiline_object: {
three: new Idea()
inner_obj: {
freedom: => _.freedom()
freedom: -> _.freedom()
}
}
# Arrays:
stooges: [{moe: 45}, {curly: 43}, {larry: 46}]
exponents: [(x => x), (x => x * x), (x => x * x * x)]
exponents: [(x) -> x, (x) -> x * x, (x) -> x * x * x]
empty: []
@@ -54,7 +54,7 @@ decoration: medal_of_honor if war_hero
go_to_sleep() unless coffee
# Returning early:
race: =>
race: ->
run()
walk()
crawl()
@@ -103,7 +103,7 @@ while true
# Lexical scoping.
v_1: 5
change_a_and_set_b: =>
change_a_and_set_b: ->
v_1: 10
v_2: 15
v_2: 20
@@ -128,7 +128,7 @@ activity: switch day
else go_to_work()
# Semicolons can optionally be used instead of newlines.
wednesday: => eat_breakfast(); go_to_work(); eat_dinner()
wednesday: -> eat_breakfast(); go_to_work(); eat_dinner()
# Array slice literals.
zero_to_nine: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
@@ -140,19 +140,19 @@ sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad."
# Inheritance and calling super.
Animal: =>
Animal::move: meters =>
Animal: ->
Animal::move: (meters) ->
alert(this.name + " moved " + meters + "m.")
Snake: name => this.name: name
Snake: (name) -> this.name: name
Snake extends Animal
Snake::move: =>
Snake::move: ->
alert('Slithering...')
super(5)
Horse: name => this.name: name
Horse: (name) -> this.name: name
Horse extends Animal
Horse::move: =>
Horse::move: ->
alert('Galloping...')
super(45)

View File

@@ -0,0 +1,4 @@
Ported from Nicholas Zakas' collection of computer science fundamentals, written
in JavaScript. Originals available here:
http://github.com/nzakas/computer-science-in-javascript

View File

@@ -0,0 +1,25 @@
# Uses a binary search algorithm to locate a value in the specified array.
binary_search: (items, value) ->
start: 0
stop: items.length - 1
pivot: Math.floor((start + stop) / 2)
while items[pivot] isnt value and start < stop
# Adjust the search area.
stop: pivot - 1 if value < items[pivot]
start: pivot + 1 if value > items[pivot]
# Recalculate the pivot.
pivot: Math.floor((stop + start) / 2)
# Make sure we've found the correct value.
if items[pivot] is value then pivot else -1
# Test the function.
print(2 is binary_search([10, 20, 30, 40, 50], 30))
print(4 is binary_search([-97, 35, 67, 88, 1200], 1200))
print(0 is binary_search([0, 45, 70], 0))
print(-1 is binary_search([0, 45, 70], 10))

View File

@@ -0,0 +1,11 @@
# A bubble sort implementation, sorting the given array in-place.
bubble_sort: (list) ->
for i in [0...list.length]
for j in [0...list.length - i]
[list[j], list[j+1]]: [list[j+1], list[j]] if list[j] > list[j+1]
list
# Test the function.
print(bubble_sort([3, 2, 1]).join(' ') is '1 2 3')
print(bubble_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9')

View File

@@ -0,0 +1,106 @@
# "Classic" linked list implementation that doesn't keep track of its size.
LinkedList: ->
this._head: null # Pointer to the first item in the list.
# Appends some data to the end of the list. This method traverses the existing
# list and places the value at the end in a new node.
LinkedList::add: (data) ->
# Create a new node object to wrap the data.
node: {data: data, next: null}
current: this._head ||= node
if this._head isnt node
current: current.next while current.next
current.next: node
this
# Retrieves the data at the given position in the list.
LinkedList::item: (index) ->
# Check for out-of-bounds values.
return null if index < 0
current: this._head or null
i: -1
# Advance through the list.
current: current.next while current and index > (i += 1)
# Return null if we've reached the end.
current and current.data
# Remove the item from the given location in the list.
LinkedList::remove: (index) ->
# Check for out-of-bounds values.
return null if index < 0
current: this._head or null
i: -1
# Special case: removing the first item.
if index is 0
this._head: current.next
else
# Find the right location.
[previous, current]: [current, current.next] while index > (i += 1)
# Skip over the item to remove.
previous.next: current.next
# Return the value.
current and current.data
# Calculate the number of items in the list.
LinkedList::size: ->
current: this._head
count: 0
while current
count += 1
current: current.next
count
# Convert the list into an array.
LinkedList::toArray: ->
result: []
current: this._head
while current
result.push(current.data)
current: current.next
result
# The string representation of the linked list.
LinkedList::toString: -> this.toArray().toString()
# Tests.
list: new LinkedList()
list.add("Hi")
print(list.size() is 1)
print(list.item(0) is "Hi")
print(list.item(1) is null)
list: new LinkedList()
list.add("zero").add("one").add("two")
print(list.size() is 3)
print(list.item(2) is "two")
print(list.remove(1) is "one")
print(list.item(0) is "zero")
print(list.item(1) is "two")
print(list.size() is 2)
print(list.item(-10) is null)

View File

@@ -0,0 +1,36 @@
# Use the Luhn algorithm to validate a numeric identifier, such as credit card
# numbers, national insurance numbers, etc.
# See: http://en.wikipedia.org/wiki/Luhn_algorithm
is_valid_identifier: (identifier) ->
sum: 0
alt: false
for i in [(identifier.length - 1)..0]
# Get the next digit.
num: parseInt(identifier.charAt(i), 10)
# If it's not a valid number, abort.
return false if isNaN(num)
# If it's an alternate number...
if alt
num *= 2
num: (num % 10) + 1 if num > 9
# Flip the alternate bit.
alt: !alt
# Add to the rest of the sum.
sum += num
# Determine if it's valid.
sum % 10 is 0
# Tests.
print(is_valid_identifier("49927398716") is true)
print(is_valid_identifier("4408041234567893") is true)
print(is_valid_identifier("4408041234567890") is false)

View File

@@ -0,0 +1,19 @@
# Sorts an array in ascending natural order using merge sort.
merge_sort: (list) ->
return list if list.length is 1
result: []
pivot: Math.floor(list.length / 2)
left: merge_sort(list.slice(0, pivot))
right: merge_sort(list.slice(pivot))
while left.length and right.length
result.push(if left[0] < right[0] then left.shift() else right.shift())
result.concat(left).concat(right)
# Test the function.
print(merge_sort([3, 2, 1]).join(' ') is '1 2 3')
print(merge_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9')

View File

@@ -0,0 +1,23 @@
# An in-place selection sort.
selection_sort: (list) ->
len: list.length
# For each item in the list.
for i in [0...len]
# Set the minimum to this position.
min: i
# Check the rest of the array to see if anything is smaller.
(min: j if list[j] < list[min]) for j in [i+1...len]
# Swap if a smaller item has been found.
[list[i], list[min]]: [list[min], list[i]] if i isnt min
# The list is now sorted.
list
# Test the function.
print(selection_sort([3, 2, 1]).join(' ') is '1 2 3')
print(selection_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9')

View File

@@ -1,72 +0,0 @@
# Document Model
dc.model.Document: dc.Model.extend({
constructor: attributes => this.base(attributes)
# For display, show either the highlighted search results, or the summary,
# if no highlights are available.
# The import process will take care of this in the future, but the inline
# version of the summary has all runs of whitespace squeezed out.
displaySummary: =>
text: this.get('highlight') or this.get('summary') or ''
text and text.replace(/\s+/g, ' ')
# Return a list of the document's metadata. Think about caching this on the
# document by binding to Metadata, instead of on-the-fly.
metadata: =>
docId: this.id
_.select(Metadata.models(), (meta =>
_.any(meta.get('instances'), instance =>
instance.document_id is docId)))
bookmark: pageNumber =>
bookmark: new dc.model.Bookmark({title: this.get('title'), page_number: pageNumber, document_id: this.id})
Bookmarks.create(bookmark)
# Inspect.
toString: => 'Document ' + this.id + ' "' + this.get('title') + '"'
})
# Document Set
dc.model.DocumentSet: dc.model.RESTfulSet.extend({
resource: 'documents'
SELECTION_CHANGED: 'documents:selection_changed'
constructor: options =>
this.base(options)
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText')
selected: => _.select(this.models(), m => m.get('selected'))
selectedIds: => _.pluck(this.selected(), 'id')
countSelected: => this.selected().length
downloadSelectedViewers: =>
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip')
downloadSelectedPDF: =>
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('pdf_url'))
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip')
downloadSelectedFullText: =>
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('full_text_url'))
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip')
# We override "_onModelEvent" to fire selection changed events when documents
# change their selected state.
_onModelEvent: e, model =>
this.base(e, model)
fire: e == dc.Model.CHANGED and model.hasChanged('selected')
if fire then _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this))
})
# The main set of Documents, used by the search tab.
window.Documents: new dc.model.DocumentSet()
# The set of documents that is used to look at a particular label.
dc.app.LabeledDocuments: new dc.model.DocumentSet()

View File

@@ -2,7 +2,7 @@
# ['toast', 'cheese', 'wine'].each { |food| print food.capitalize }
['toast', 'wine', 'cheese'].each(food => print(food.capitalize()))
['toast', 'wine', 'cheese'].each (food) -> print(food.capitalize())
@@ -14,10 +14,43 @@
# end
LotteryTicket: {
get_picks: => this.picks
set_picks: nums => this.picks: nums
get_purchase: => this.purchase
set_purchase: amount => this.purchase: amount
get_picks: -> this.picks
set_picks: (nums) -> this.picks: nums
get_purchase: -> this.purchase
set_purchase: (amount) -> this.purchase: amount
}
# class << LotteryDraw
# def play
# result = LotteryTicket.new_random
# winners = {}
# @@tickets.each do |buyer, ticket_list|
# ticket_list.each do |ticket|
# score = ticket.score( result )
# next if score.zero?
# winners[buyer] ||= []
# winners[buyer] << [ ticket, score ]
# end
# end
# @@tickets.clear
# winners
# end
# end
LotteryDraw: {
play: ->
result: LotteryTicket.new_random()
winners: {}
this.tickets.each (buyer, ticket_list) ->
ticket_list.each (ticket) ->
score: ticket.score(result)
return if score is 0
winners[buyer] ||= []
winners[buyer].push([ticket, score])
this.tickets: {}
winners
}
@@ -32,8 +65,8 @@ LotteryTicket: {
# end
WishScanner: {
scan_for_a_wish: =>
wish: this.read().detect(thought => thought.index('wish: ') is 0)
scan_for_a_wish: ->
wish: this.read().detect((thought) -> thought.index('wish: ') is 0)
wish.replace('wish: ', '')
}
@@ -78,7 +111,7 @@ WishScanner: {
Creature : {
# This method applies a hit taken during a fight.
hit: damage =>
hit: (damage) ->
p_up: Math.rand(this.charisma)
if p_up % 9 is 7
this.life += p_up / 4
@@ -87,7 +120,7 @@ Creature : {
if this.life <= 0 then puts("[" + this.name + " has died.]")
# This method takes one turn in a fight.
fight: enemy, weapon =>
fight: (enemy, weapon) ->
if this.life <= 0 then return puts("[" + this.name + "is too dead to fight!]")
# Attack the opponent.
@@ -123,12 +156,12 @@ Creature : {
# Get evil idea and swap in code words
print("Enter your new idea: ")
idea: gets()
code_words.each(real, code => idea.replace(real, code))
code_words.each((real, code) -> idea.replace(real, code))
# Save the jibberish to a new file
print("File encoded. Please enter a name for this idea: ")
idea_name: gets().strip()
File.open("idea-" + idea_name + '.txt', 'w', file => file.write(idea))
File.open("idea-" + idea_name + '.txt', 'w', (file) -> file.write(idea))
@@ -144,7 +177,7 @@ File.open("idea-" + idea_name + '.txt', 'w', file => file.write(idea))
# end
# end
wipe_mutterings_from: sentence =>
wipe_mutterings_from: (sentence) ->
throw new Error("cannot wipe mutterings") unless sentence.indexOf
while sentence.indexOf('(') >= 0
open: sentence.indexOf('(') - 1

View File

@@ -8,7 +8,7 @@ print("Odelay!") for i in [1..5]
# add = (x, y): x + y.
# add(2, 4) string print
add: x, y => x + y
add: (x, y) -> x + y
print(add(2, 4))
@@ -31,7 +31,7 @@ print({language: 'Potion', pointless: true}['language'])
# minus = (x, y): x - y.
# minus (y=10, x=6)
minus: x, y => x - y
minus: (x, y) -> x - y
minus(6, 10)
@@ -53,8 +53,8 @@ for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
# Person print = ():
# ('My name is ', /name, '.') join print.
Person: =>
Person::print: =>
Person: ->
Person::print: ->
print('My name is ' + this.name + '.')
@@ -71,9 +71,9 @@ print(p.name)
#
# Policeman ('Constable') print
Policeman: rank => this.rank: rank
Policeman: (rank) -> this.rank: rank
Policeman extends Person
Policeman::print: =>
Policeman::print: ->
print('My name is ' + this.name + " and I'm a " + this.rank + '.')
print(new Policeman('Constable'))
@@ -115,13 +115,13 @@ table: {
# String length = (): 10.
# this foul business...
String::length: => 10
String::length: -> 10
# block = :
# 'potion' print.
block: =>
block: ->
print('potion')
@@ -178,7 +178,7 @@ if (3).gender?
# HomePage get = (url):
# session = url query ? at ('session').
HomePage::get: url =>
HomePage::get: (url) ->
session: url.query.session if url.query?
@@ -187,7 +187,7 @@ HomePage::get: url =>
# b /left = BTree ()
# b /right = BTree ()
BTree: =>
BTree: ->
b: new BTree()
b.left: new BTree()
b.right: new BTree()
@@ -199,7 +199,7 @@ b.right: new BTree()
# if (b ? /left):
# 'left path found!' print.
BTree: =>
BTree: ->
b: new BTree()
print('left path found!') if b.left?

View File

@@ -1,20 +0,0 @@
# Identifiers run together:
# a b c
# Trailing comma in array:
# array: [1, 2, 3, 4, 5,]
# Unterminated object literal:
# obj: { one: 1, two: 2
# Numbers run together:
# 101 202
# Strings run together:
# str: "broken" "words"
# Forgot to terminate a function:
# obj: {
# first: a => a[0].
# last: a => a[a.length-1]
# }

View File

@@ -1,6 +1,6 @@
# Underscore.coffee
# (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
# (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.
@@ -21,7 +21,7 @@
# 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
# underscore functions. Wrapped objects may be chained.
wrapper: obj =>
wrapper: (obj) ->
this._wrapped: obj
this
@@ -31,7 +31,7 @@
# Create a safe reference to the Underscore object forreference below.
_: root._: obj => new wrapper(obj)
_: root._: (obj) -> new wrapper(obj)
# Export the Underscore object for CommonJS.
@@ -47,14 +47,14 @@
# Current version.
_.VERSION: '0.5.5'
_.VERSION: '0.5.7'
# ------------------------ Collection Functions: ---------------------------
# The cornerstone, an each implementation.
# Handles objects implementing forEach, arrays, and raw objects.
_.each: obj, iterator, context =>
_.each: (obj, iterator, context) ->
index: 0
try
return obj.forEach(iterator, context) if obj.forEach
@@ -68,36 +68,36 @@
# Return the results of applying the iterator to each element. Use JavaScript
# 1.6's version of map, if possible.
_.map: obj, iterator, context =>
_.map: (obj, iterator, context) ->
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
results: []
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
results.push(iterator.call(context, value, index, list))
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: obj, memo, iterator, context =>
_.reduce: (obj, memo, iterator, context) ->
return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
memo: iterator.call(context, memo, value, index, list)
memo
# 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 =>
_.reduceRight: (obj, memo, iterator, context) ->
return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
_.each(_.clone(_.toArray(obj)).reverse()) value, index =>
_.each _.clone(_.toArray(obj)).reverse(), (value, index) ->
memo: iterator.call(context, memo, value, index, obj)
memo
# Return the first value which passes a truth test.
_.detect: obj, iterator, context =>
_.detect: (obj, iterator, context) ->
result: null
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
if iterator.call(context, value, index, list)
result: value
_.breakLoop()
@@ -106,47 +106,47 @@
# Return all the elements that pass a truth test. Use JavaScript 1.6's
# filter(), if it exists.
_.select: obj, iterator, context =>
_.select: (obj, iterator, context) ->
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context)
results: []
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
results.push(value) if iterator.call(context, value, index, list)
results
# Return all the elements for which a truth test fails.
_.reject: obj, iterator, context =>
_.reject: (obj, iterator, context) ->
results: []
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
results.push(value) if not iterator.call(context, value, index, list)
results
# Determine whether all of the elements match a truth test. Delegate to
# JavaScript 1.6's every(), if it is present.
_.all: obj, iterator, context =>
_.all: (obj, iterator, context) ->
iterator ||= _.identity
return obj.every(iterator, context) if obj and _.isFunction(obj.every)
result: true
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
_.breakLoop() unless (result: result and iterator.call(context, value, index, list))
result
# Determine if at least one element in the object matches a truth test. Use
# JavaScript 1.6's some(), if it exists.
_.any: obj, iterator, context =>
_.any: (obj, iterator, context) ->
iterator ||= _.identity
return obj.some(iterator, context) if obj and _.isFunction(obj.some)
result: false
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
_.breakLoop() if (result: iterator.call(context, value, index, list))
result
# Determine if a given value is included in the array or object,
# based on '==='.
_.include: obj, target =>
_.include: (obj, target) ->
return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
for key, val of obj
return true if val is target
@@ -154,49 +154,49 @@
# Invoke a method with arguments on every item in a collection.
_.invoke: obj, method =>
_.invoke: (obj, method) ->
args: _.rest(arguments, 2)
(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.
_.pluck: obj, key =>
_.map(obj, (val => val[key]))
_.pluck: (obj, key) ->
_.map(obj, ((val) -> val[key]))
# Return the maximum item or (item-based computation).
_.max: obj, iterator, context =>
_.max: (obj, iterator, context) ->
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
result: {computed: -Infinity}
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
computed: if iterator then iterator.call(context, value, index, list) else value
computed >= result.computed and (result: {value: value, computed: computed})
result.value
# Return the minimum element (or element-based computation).
_.min: obj, iterator, context =>
_.min: (obj, iterator, context) ->
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
result: {computed: Infinity}
_.each(obj) value, index, list =>
_.each obj, (value, index, list) ->
computed: if iterator then iterator.call(context, value, index, list) else value
computed < result.computed and (result: {value: value, computed: computed})
result.value
# Sort the object's values by a criteria produced by an iterator.
_.sortBy: obj, iterator, context =>
_.pluck(((_.map(obj) value, index, list =>
_.sortBy: (obj, iterator, context) ->
_.pluck(((_.map obj, (value, index, list) ->
{value: value, criteria: iterator.call(context, value, index, list)}
).sort() left, right =>
).sort((left, right) ->
a: left.criteria; b: right.criteria
if a < b then -1 else if a > b then 1 else 0
), 'value')
)), 'value')
# Use a comparator function to figure out at what index an object should
# be inserted so as to maintain order. Uses binary search.
_.sortedIndex: array, obj, iterator =>
_.sortedIndex: (array, obj, iterator) ->
iterator ||= _.identity
low: 0; high: array.length
while low < high
@@ -206,7 +206,7 @@
# Convert anything iterable into a real, live array.
_.toArray: iterable =>
_.toArray: (iterable) ->
return [] if (!iterable)
return iterable.toArray() if (iterable.toArray)
return iterable if (_.isArray(iterable))
@@ -215,7 +215,7 @@
# Return the number of elements in an object.
_.size: obj => _.toArray(obj).length
_.size: (obj) -> _.toArray(obj).length
# -------------------------- Array Functions: ------------------------------
@@ -223,7 +223,7 @@
# 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 =>
_.first: (array, n, guard) ->
if n and not guard then slice.call(array, 0, n) else array[0]
@@ -231,35 +231,35 @@
# 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 =>
_.rest: (array, index, guard) ->
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
# Get the last element of an array.
_.last: array => array[array.length - 1]
_.last: (array) -> array[array.length - 1]
# Trim out all falsy values from an array.
_.compact: array => array[i] for i in [0...array.length] when array[i]
_.compact: (array) -> array[i] for i in [0...array.length] when array[i]
# Return a completely flattened version of an array.
_.flatten: array =>
_.reduce(array, []) memo, value =>
_.flatten: (array) ->
_.reduce array, [], (memo, value) ->
return memo.concat(_.flatten(value)) if _.isArray(value)
memo.push(value)
memo
# Return a version of the array that does not contain the specified value(s).
_.without: array =>
_.without: (array) ->
values: _.rest(arguments)
val for val in _.toArray(array) when not _.include(values, val)
# Produce a duplicate-free version of the array. If the array has already
# been sorted, you have the option of using a faster algorithm.
_.uniq: array, isSorted =>
_.uniq: (array, isSorted) ->
memo: []
for el, i in _.toArray(array)
memo.push(el) if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
@@ -268,28 +268,27 @@
# Produce an array that contains every item shared between all the
# passed-in arrays.
_.intersect: array =>
_.intersect: (array) ->
rest: _.rest(arguments)
_.select(_.uniq(array)) item =>
_.all(rest) other =>
_.select _.uniq(array), (item) ->
_.all rest, (other) ->
_.indexOf(other, item) >= 0
# Zip together multiple lists into a single array -- elements that share
# an index go together.
_.zip: =>
args: _.toArray(arguments)
length: _.max(_.pluck(args, 'length'))
_.zip: ->
length: _.max(_.pluck(arguments, 'length'))
results: new Array(length)
for i in [0...length]
results[i]: _.pluck(args, String(i))
results[i]: _.pluck(arguments, String(i))
results
# 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 =>
_.indexOf: (array, item) ->
return array.indexOf(item) if array.indexOf
i: 0; l: array.length
while l - i
@@ -299,7 +298,7 @@
# Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
# if possible.
_.lastIndexOf: array, item =>
_.lastIndexOf: (array, item) ->
return array.lastIndexOf(item) if array.lastIndexOf
i: array.length
while i
@@ -310,8 +309,8 @@
# 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
_.range: start, stop, step =>
a: _.toArray(arguments)
_.range: (start, stop, step) ->
a: arguments
solo: a.length <= 1
i: start: if solo then 0 else a[0];
stop: if solo then a[0] else a[1];
@@ -331,45 +330,45 @@
# Create a function bound to a given object (assigning 'this', and arguments,
# optionally). Binding with arguments is also known as 'curry'.
_.bind: func, obj =>
_.bind: (func, obj) ->
args: _.rest(arguments, 2)
=> func.apply(obj or root, args.concat(_.toArray(arguments)))
-> func.apply(obj or root, args.concat(arguments))
# Bind all of an object's methods to that object. Useful for ensuring that
# all callbacks defined on an object belong to it.
_.bindAll: obj =>
_.bindAll: (obj) ->
funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
_.each(funcs, (f => obj[f]: _.bind(obj[f], obj)))
_.each(funcs, (f) -> obj[f]: _.bind(obj[f], obj))
obj
# Delays a function for the given number of milliseconds, and then calls
# it with the arguments supplied.
_.delay: func, wait =>
_.delay: (func, wait) ->
args: _.rest(arguments, 2)
setTimeout((=> func.apply(func, args)), wait)
setTimeout((-> func.apply(func, args)), wait)
# Defers a function, scheduling it to run after the current call stack has
# cleared.
_.defer: func =>
_.defer: (func) ->
_.delay.apply(_, [func, 1].concat(_.rest(arguments)))
# Returns the first function passed as an argument to the second,
# allowing you to adjust arguments, run code before and after, and
# conditionally execute the original function.
_.wrap: func, wrapper =>
=> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))
_.wrap: (func, wrapper) ->
-> wrapper.apply(wrapper, [func].concat(arguments))
# Returns a function that is the composition of a list of functions, each
# consuming the return value of the function that follows.
_.compose: =>
funcs: _.toArray(arguments)
=>
args: _.toArray(arguments)
_.compose: ->
funcs: arguments
->
args: arguments
for i in [(funcs.length - 1)..0]
args: [funcs[i].apply(this, args)]
args[0]
@@ -378,43 +377,43 @@
# ------------------------- Object Functions: ----------------------------
# Retrieve the names of an object's properties.
_.keys: obj =>
_.keys: (obj) ->
return _.range(0, obj.length) if _.isArray(obj)
key for key, val of obj
# Retrieve the values of an object's properties.
_.values: obj =>
_.values: (obj) ->
_.map(obj, _.identity)
# Return a sorted list of the function names available in Underscore.
_.functions: obj =>
_.select(_.keys(obj), key => _.isFunction(obj[key])).sort()
_.functions: (obj) ->
_.select(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
# Extend a given object with all of the properties in a source object.
_.extend: destination, source =>
_.extend: (destination, source) ->
for key, val of source
destination[key]: val
destination
# Create a (shallow-cloned) duplicate of an object.
_.clone: obj =>
_.clone: (obj) ->
return obj.slice(0) if _.isArray(obj)
_.extend({}, obj)
# Invokes interceptor with the obj, and then returns obj.
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
_.tap: obj, interceptor =>
_.tap: (obj, interceptor) ->
interceptor(obj)
obj
# Perform a deep comparison to check if two objects are equal.
_.isEqual: a, b =>
_.isEqual: (a, b) ->
# Check object identity.
return true if a is b
# Different types?
@@ -450,93 +449,104 @@
# Is a given array or object empty?
_.isEmpty: obj => _.keys(obj).length is 0
_.isEmpty: (obj) -> _.keys(obj).length is 0
# Is a given value a DOM element?
_.isElement: obj => obj and obj.nodeType is 1
_.isElement: (obj) -> obj and obj.nodeType is 1
# Is a given value an array?
_.isArray: obj => !!(obj and obj.concat and obj.unshift)
_.isArray: (obj) -> !!(obj and obj.concat and obj.unshift)
# Is a given variable an arguments object?
_.isArguments: obj => obj and _.isNumber(obj.length) and !_.isArray(obj) and !propertyIsEnumerable.call(obj, 'length')
_.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')
# Is the given value a function?
_.isFunction: obj => !!(obj and obj.constructor and obj.call and obj.apply)
_.isFunction: (obj) -> !!(obj and obj.constructor and obj.call and obj.apply)
# Is the given value a string?
_.isString: obj => !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
_.isString: (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
# Is a given value a number?
_.isNumber: obj => toString.call(obj) is '[object Number]'
_.isNumber: (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
# Is a given value a Date?
_.isDate: obj => !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
_.isDate: (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
# Is the given value a regular expression?
_.isRegExp: obj => !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
_.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.
_.isNaN: obj => _.isNumber(obj) and window.isNaN(obj)
_.isNaN: (obj) -> _.isNumber(obj) and window.isNaN(obj)
# Is a given value equal to null?
_.isNull: obj => obj is null
_.isNull: (obj) -> obj is null
# Is a given variable undefined?
_.isUndefined: obj => typeof obj is 'undefined'
_.isUndefined: (obj) -> typeof obj is 'undefined'
# -------------------------- Utility Functions: --------------------------
# Run Underscore.js in noConflict mode, returning the '_' variable to its
# previous owner. Returns a reference to the Underscore object.
_.noConflict: =>
_.noConflict: ->
root._: previousUnderscore
this
# Keep the identity function around for default iterators.
_.identity: value => value
_.identity: (value) -> value
# Break out of the middle of an iteration.
_.breakLoop: => throw breaker
_.breakLoop: -> throw breaker
# Generate a unique integer id (unique within the entire client session).
# Useful for temporary DOM ids.
idCounter: 0
_.uniqueId: prefix =>
_.uniqueId: (prefix) ->
(prefix or '') + idCounter++
# By default, Underscore uses ERB-style template delimiters, change the
# following template settings to use alternative delimiters.
_.templateSettings: {
start: '<%'
end: '%>'
interpolate: /<%=(.+?)%>/g
}
# JavaScript templating a-la ERB, pilfered from John Resig's
# "Secrets of the JavaScript Ninja", page 83.
_.template: str, data =>
`var fn = new Function('obj',
# Single-quotea fix from Rick Strahl's version.
_.template: (str, data) ->
c: _.templateSettings
fn: new Function 'obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj){p.push(\'' +
str.
replace(/[\r\t\n]/g, " ").
split("<%").join("\t").
replace(/((^|%>)[^\t]*)'/g, "$1\r").
replace(/\t=(.*?)%>/g, "',$1,'").
split("\t").join("');").
split("%>").join("p.push('").
split("\r").join("\\'") +
"');}return p.join('');")`
str.replace(/[\r\t\n]/g, " ")
.replace(new RegExp("'(?=[^"+c.end[0]+"]*"+c.end+")","g"),"\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(c.interpolate, "',$1,'")
.split(c.start).join("');")
.split(c.end).join("p.push('") +
"');}return p.join('');"
if data then fn(data) else fn
@@ -556,39 +566,38 @@
# /*------------------------ Setup the OOP Wrapper: --------------------------*/
# Helper function to continue chaining intermediate results.
result: obj, chain =>
result: (obj, chain) ->
if chain then _(obj).chain() else obj
# Add all of the Underscore functions to the wrapper object.
_.each(_.functions(_)) name =>
_.each _.functions(_), (name) ->
method: _[name]
wrapper.prototype[name]: =>
args: _.toArray(arguments)
unshift.call(args, this._wrapped)
result(method.apply(_, args), this._chain)
wrapper.prototype[name]: ->
unshift.call(arguments, this._wrapped)
result(method.apply(_, arguments), this._chain)
# Add all mutator Array functions to the wrapper.
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift']) name =>
_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->
method: Array.prototype[name]
wrapper.prototype[name]: =>
wrapper.prototype[name]: ->
method.apply(this._wrapped, arguments)
result(this._wrapped, this._chain)
# Add all accessor Array functions to the wrapper.
_.each(['concat', 'join', 'slice']) name =>
_.each ['concat', 'join', 'slice'], (name) ->
method: Array.prototype[name]
wrapper.prototype[name]: =>
wrapper.prototype[name]: ->
result(method.apply(this._wrapped, arguments), this._chain)
# Start chaining a wrapped Underscore object.
wrapper::chain: =>
wrapper::chain: ->
this._chain: true
this
# Extracts the result from a wrapped and chained object.
wrapper::value: => this._wrapped
wrapper::value: -> this._wrapped

View File

@@ -19,52 +19,31 @@
<dict>
<key>captures</key>
<dict>
<key>1</key>
<key>1</key>
<dict>
<key>name</key>
<string>entity.name.function.coffee</string>
<string>variable.parameter.function.coffee</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>keyword.operator.coffee</string>
</dict>
<key>3</key>
<dict>
<key>name</key>
<string>variable.parameter.function.coffee</string>
</dict>
<key>4</key>
<dict>
<key>name</key>
<string>storage.type.function.coffee</string>
</dict>
</dict>
<key>comment</key>
<string>match stuff like: funcName: =&gt; … </string>
<key>match</key>
<string>([a-zA-Z0-9_?.$:*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</string>
<key>name</key>
<string>meta.function.coffee</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.parameter.function.coffee</string>
</dict>
<key>2</key>
<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>
<string>match stuff like: a -&gt; … </string>
<key>match</key>
<string>([a-zA-Z0-9_?., $:*]*)\s*(=&gt;)</string>
<string>(\()([a-zA-Z0-9_?.$]*(,\s*[a-zA-Z0-9_?.$]+)*)(\))\s*((=|-)&gt;)</string>
<key>name</key>
<string>meta.inline.function.coffee</string>
</dict>
@@ -230,6 +209,39 @@
<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>
@@ -238,7 +250,7 @@
</dict>
<dict>
<key>match</key>
<string>\b([a-zA-Z$_](\w|\$|:|\.)*)(\:)\s</string>
<string>\b([a-zA-Z$_](\w|\$|:|\.)*\s*(?=\:))</string>
<key>name</key>
<string>variable.assignment.coffee</string>
<key>captures</key>
@@ -248,11 +260,6 @@
<key>name</key>
<string>entity.name.function.coffee</string>
</dict>
<key>3</key>
<dict>
<key>name</key>
<string>keyword.operator.coffee</string>
</dict>
</dict>
</dict>
<dict>
@@ -285,6 +292,12 @@
<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>
@@ -297,39 +310,6 @@
<key>name</key>
<string>constant.language.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>\;</string>

20
extras/EXTRAS Normal file
View File

@@ -0,0 +1,20 @@
This folder includes rough cuts of CoffeeScript syntax highlighters for
TextMate and Vim. Improvements to their lexing ability are always welcome.
To install the TextMate bundle, run `bin/coffee --install-bundle`, or 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

111
extras/coffee.vim Normal file
View File

@@ -0,0 +1,111 @@
" 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 coffeePrototypeAccess "::"
syn match coffeeFunction "->"
syn keyword coffeeExtends extends
syn keyword coffeeConditional if else switch then
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 coffeeFunction 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

View File

@@ -37,7 +37,7 @@
<p>
<b>Latest Version:</b>
<a href="http://gemcutter.org/gems/coffee-script">0.2.4</a>
<a href="http://gemcutter.org/gems/coffee-script">0.3.0</a>
</p>
<h2>Table of Contents</h2>
@@ -51,7 +51,6 @@
<a href="#objects_and_arrays">Objects and Arrays</a><br />
<a href="#lexical_scope">Lexical Scoping and Variable Safety</a><br />
<a href="#conditionals">Conditionals, Ternaries, and Conditional Assignment</a><br />
<a href="#existence">The Existence Operator</a><br />
<a href="#aliases">Aliases</a><br />
<a href="#splats">Splats...</a><br />
<a href="#arguments">Arguments are Arrays</a><br />
@@ -59,15 +58,16 @@
<a href="#comprehensions">Comprehensions (Arrays, Objects, and Ranges)</a><br />
<a href="#slice_splice">Array Slicing and Splicing with Ranges</a><br />
<a href="#expressions">Everything is an Expression</a><br />
<a href="#existence">The Existential Operator</a><br />
<a href="#inheritance">Inheritance, and Calling Super from a Subclass</a><br />
<a href="#blocks">Blocks</a><br />
<a href="#pattern_matching">Pattern Matching</a><br />
<a href="#fat_arrow">Function Binding</a><br />
<a href="#embedded">Embedded JavaScript</a><br />
<a href="#switch">Switch/When/Else</a><br />
<a href="#try">Try/Catch/Finally</a><br />
<a href="#comparisons">Chained Comparisons</a><br />
<a href="#strings">Multiline Strings and Heredocs</a><br />
<a href="#resources">Resources</a><br />
<a href="#contributing">Contributing</a><br />
<a href="#change_log">Change Log</a><br />
</p>
@@ -83,7 +83,7 @@
<span class="FunctionName">number</span><span class="Keyword">:</span> <span class="Keyword">-</span><span class="Number">42</span> <span class="Keyword">if</span> opposite_day
<span class="Comment"><span class="Comment">#</span> Functions:</span>
<span class="FunctionName">square</span><span class="Keyword">:</span> <span class="FunctionArgument">x</span> <span class="Storage">=&gt;</span> x <span class="Keyword">*</span> x
<span class="FunctionName">square</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">x</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> x <span class="Keyword">*</span> x
<span class="Comment"><span class="Comment">#</span> Arrays:</span>
<span class="FunctionName">list</span><span class="Keyword">:</span> [<span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>]
@@ -92,18 +92,18 @@
<span class="FunctionName">math</span><span class="Keyword">:</span> {
<span class="FunctionName">root</span><span class="Keyword">:</span> Math.sqrt
<span class="FunctionName">square</span><span class="Keyword">:</span> square
<span class="FunctionArgument"> cube: x </span><span class="Storage">=&gt;</span> x <span class="Keyword">*</span> square(x)
<span class="FunctionName">cube</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">x</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> x <span class="Keyword">*</span> square x
}
<span class="Comment"><span class="Comment">#</span> Splats:</span>
<span class="FunctionArgument">race: winner, runners... </span><span class="Storage">=&gt;</span>
print(winner, runners)
<span class="FunctionName">race</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">winner, runners...</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
print winner, runners
<span class="Comment"><span class="Comment">#</span> Existence:</span>
alert(<span class="String"><span class="String">&quot;</span>I knew it!<span class="String">&quot;</span></span>) <span class="Keyword">if</span> elvis<span class="Keyword">?</span>
alert <span class="String"><span class="String">&quot;</span>I knew it!<span class="String">&quot;</span></span> <span class="Keyword">if</span> elvis<span class="Keyword">?</span>
<span class="Comment"><span class="Comment">#</span> Array comprehensions:</span>
<span class="FunctionName">cubed_list</span><span class="Keyword">:</span> math.cube(num) <span class="Keyword">for</span> num <span class="Keyword">in</span> list
<span class="FunctionName">cubed_list</span><span class="Keyword">:</span> math.cube num <span class="Keyword">for</span> num <span class="Keyword">in</span> list
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, cubed_list, list, math, num, number, opposite_day, race, square;
<span class="Comment"><span class="Comment">//</span> Assignment:</span>
number <span class="Keyword">=</span> <span class="Number">42</span>;
@@ -139,12 +139,12 @@ race <span class="Keyword">=</span> <span class="Storage">function</span> <span
<span class="Comment"><span class="Comment">//</span> Array comprehensions:</span>
cubed_list <span class="Keyword">=</span> (<span class="Storage">function</span>() {
__a <span class="Keyword">=</span> []; __b <span class="Keyword">=</span> list;
<span class="Keyword">for</span> (__c<span class="Keyword">=</span><span class="Number">0</span>; __c<span class="Keyword">&lt;</span>__b.<span class="LibraryConstant">length</span>; __c<span class="Keyword">++</span>) {
<span class="Keyword">for</span> (__c <span class="Keyword">=</span> <span class="Number">0</span>; __c <span class="Keyword">&lt;</span> __b.<span class="LibraryConstant">length</span>; __c<span class="Keyword">++</span>) {
num <span class="Keyword">=</span> __b[__c];
__a.<span class="LibraryFunction">push</span>(math.cube(num));
}
<span class="Keyword">return</span> __a;
})();
}).<span class="LibraryFunction">call</span>(<span class="Variable">this</span>);
</pre><button onclick='javascript: var __a, __b, __c, cubed_list, list, math, num, number, opposite_day, race, square;
// Assignment:
number = 42;
@@ -180,12 +180,12 @@ if ((typeof elvis !== "undefined" && elvis !== null)) {
// Array comprehensions:
cubed_list = (function() {
__a = []; __b = list;
for (__c=0; __c<__b.length; __c++) {
for (__c = 0; __c < __b.length; __c++) {
num = __b[__c];
__a.push(math.cube(num));
}
return __a;
})();
}).call(this);
;alert(cubed_list);'>run: cubed_list</button><br class='clear' /></div>
<p>
@@ -269,7 +269,7 @@ gem install coffee-script</pre>
<td><code>-e, --eval</code></td>
<td>
Compile and print a little snippet of CoffeeScript directly from the
command line (or from <b>stdin</b>). For example:<br /><tt>coffee -e "square: x => x * x"</tt>
command line (or from <b>stdin</b>). For example:<br /><tt>coffee -e "square: (x) -> x * x"</tt>
</td>
</tr>
<tr>
@@ -283,7 +283,7 @@ gem install coffee-script</pre>
<td><code>-v, --verbose</code></td>
<td>
As the JavaScript is being generated, print out every step of code
generation, including lexical scope and the node in the
generation, including lexical scope and the nodes in the
AST.
</td>
</tr>
@@ -343,21 +343,24 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
use indentation.
</p>
<p>
You don't need to use parentheses to invoke a function, if you're passing
arguments:<br /><tt>print "coffee"</tt>
</p>
<p>
You can use newlines to break up your expression into smaller pieces,
as long as CoffeeScript can tell that the line hasn't finished
(similar to how Ruby handles it). For example,
if the line ends in an operator, dot, or keyword.
as long as CoffeeScript can determine that the line hasn't finished yet.
</p>
<p id="functions">
<b class="header">Functions and Invocation</b>
Functions are defined by a list of parameters, an arrow, and the
function body. The empty function looks like this: <tt>=></tt>. All
functions in CoffeeScript are named, for the benefit of debug messages.
function body. The empty function looks like this: <tt>-></tt>. All
functions in CoffeeScript are named by default, for easier debugging.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">square</span><span class="Keyword">:</span> <span class="FunctionArgument">x</span> <span class="Storage">=&gt;</span> x <span class="Keyword">*</span> x
<span class="FunctionName">cube</span><span class="Keyword">:</span> <span class="FunctionArgument">x</span> <span class="Storage">=&gt;</span> square(x) <span class="Keyword">*</span> x
<div class='code'><pre class="idle"><span class="FunctionName">square</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">x</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> x <span class="Keyword">*</span> x
<span class="FunctionName">cube</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">x</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> square(x) <span class="Keyword">*</span> x
</pre><pre class="idle"><span class="Storage">var</span> cube, square;
square <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">square</span>(<span class="FunctionArgument">x</span>) {
<span class="Keyword">return</span> x <span class="Keyword">*</span> x;
@@ -373,6 +376,10 @@ cube = function cube(x) {
return square(x) * x;
};
;alert(cube(5));'>run: cube(5)</button><br class='clear' /></div>
<p>
If you'd like to create an anonymous function, just wrap it in parentheses:
<tt>((x) -> x * x)</tt>
</p>
<p id="assignment">
<b class="header">Assignment</b>
@@ -390,7 +397,7 @@ greeting = "Hello CoffeeScript";
difficulty = 0.5;
;alert(greeting);'>run: greeting</button><br class='clear' /></div>
<p>
Declarations of new variables are pushed up to the top of the nearest
Declaration of new variables are pushed up to the top of the nearest
lexical scope, so that assignment may always be performed within expressions.
</p>
@@ -399,7 +406,7 @@ difficulty = 0.5;
Object and Array literals look very similar to their JavaScript cousins.
When you spread out each assignment on a separate line, the commas are
optional. In this way, assigning object properties looks the same as
assigning local variables, and can be moved around freely. You can mix
assigning local variables, and can be moved around freely. Feel free to mix
and match the two styles.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">song</span><span class="Keyword">:</span> [<span class="String"><span class="String">&quot;</span>do<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>re<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>mi<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>fa<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>so<span class="String">&quot;</span></span>]
@@ -440,7 +447,7 @@ matrix = [1, 0, 1, 0, 0, 1, 1, 1, 0];
<tt>var</tt> yourself.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">num</span><span class="Keyword">:</span> <span class="Number">1</span>
<span class="FunctionName">change_numbers</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
<span class="FunctionName">change_numbers</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="FunctionName">new_num</span><span class="Keyword">:</span> <span class="Keyword">-</span><span class="Number">1</span>
<span class="FunctionName">num</span><span class="Keyword">:</span> <span class="Number">10</span>
<span class="FunctionName">new_num</span><span class="Keyword">:</span> change_numbers()
@@ -474,9 +481,14 @@ new_num = change_numbers();
CoffeeScript output is wrapped in an anonymous function:
<tt>(function(){ ... })();</tt> This safety wrapper, combined with the
automatic generation of the <tt>var</tt> keyword, make it exceedingly difficult
to pollute the global namespace by accident. If you'd like to create
global variables, attach them as properties on <b>window</b>,
or on the <b>exports</b> object in CommonJS.
to pollute the global namespace by accident.
</p>
<p>
If you'd like to create top-level variables for other scripts to use,
attach them as properties on <b>window</b>, or on the <b>exports</b>
object in CommonJS. The <b>existential operator</b> (below), gives you a
reliable way to figure out where to add them, if you're targeting both
CommonJS and the browser: <tt>root: exports ? this</tt>
</p>
<p id="conditionals">
@@ -499,7 +511,7 @@ new_num = change_numbers();
<span class="FunctionName">date</span><span class="Keyword">:</span> <span class="Keyword">if</span> friday <span class="Keyword">then</span> sue <span class="Keyword">else</span> jill
expensive <span class="Keyword">||</span><span class="Keyword">=</span> do_the_math()
</pre><pre class="idle"><span class="Storage">var</span> date, mood;
</pre><pre class="idle"><span class="Storage">var</span> date, expensive, mood;
<span class="Keyword">if</span> (singing) {
mood <span class="Keyword">=</span> greatly_improved;
}
@@ -517,21 +529,6 @@ expensive <span class="Keyword">=</span> expensive <span class="Keyword">||</spa
truthy variables.
</p>
<p id="existence">
<b class="header">The Existence Operator</b>
It's a little difficult to check for the existence of a variable in
JavaScript. <tt>if (variable) ...</tt> comes close, but fails for zero,
the empty string, and false. The existence operator <tt>?</tt> returns true unless
a variable is <b>null</b> or <b>undefined</b>, which makes it analogous
to Ruby's <tt>nil?</tt>
</p>
<div class='code'><pre class="idle"><span class="FunctionName">solipsism</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> mind<span class="Keyword">?</span> <span class="Keyword">and</span> <span class="Keyword">not</span> world<span class="Keyword">?</span>
</pre><pre class="idle"><span class="Storage">var</span> solipsism;
<span class="Keyword">if</span> ((<span class="Keyword">typeof</span> mind <span class="Keyword">!</span><span class="Keyword">==</span> <span class="String"><span class="String">&quot;</span>undefined<span class="String">&quot;</span></span> <span class="Keyword">&amp;</span><span class="Keyword">&amp;</span> mind <span class="Keyword">!</span><span class="Keyword">==</span> <span class="BuiltInConstant">null</span>) <span class="Keyword">&amp;</span><span class="Keyword">&amp;</span> <span class="Keyword">!</span>(<span class="Keyword">typeof</span> world <span class="Keyword">!</span><span class="Keyword">==</span> <span class="String"><span class="String">&quot;</span>undefined<span class="String">&quot;</span></span> <span class="Keyword">&amp;</span><span class="Keyword">&amp;</span> world <span class="Keyword">!</span><span class="Keyword">==</span> <span class="BuiltInConstant">null</span>)) {
solipsism <span class="Keyword">=</span> <span class="BuiltInConstant">true</span>;
}
</pre><br class='clear' /></div>
<p id="aliases">
<b class="header">Aliases</b>
Because the <tt>==</tt> operator frequently causes undesirable coercion,
@@ -585,11 +582,11 @@ car.speed <span class="Keyword">&lt;</span> speed_limit ? accelerate() : <span c
The JavaScript <b>arguments object</b> is a useful way to work with
functions that accept variable numbers of arguments. CoffeeScript provides
splats <tt>...</tt>, both for function definition as well as invocation,
making variable arguments a little bit more palatable.
making variable numbers of arguments a little bit more palatable.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">gold</span><span class="Keyword">:</span> <span class="FunctionName">silver</span><span class="Keyword">:</span> <span class="FunctionName">the_field</span><span class="Keyword">:</span> <span class="String"><span class="String">&quot;</span>unknown<span class="String">&quot;</span></span>
<span class="FunctionArgument">medalists: first, second, rest... </span><span class="Storage">=&gt;</span>
<span class="FunctionName">award_medals</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">first, second, rest...</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="FunctionName">gold</span><span class="Keyword">:</span> first
<span class="FunctionName">silver</span><span class="Keyword">:</span> second
<span class="FunctionName">the_field</span><span class="Keyword">:</span> rest
@@ -607,14 +604,14 @@ car.speed <span class="Keyword">&lt;</span> speed_limit ? accelerate() : <span c
<span class="String"><span class="String">&quot;</span>Usain Bolt<span class="String">&quot;</span></span>
]
medalists(contenders...)
award_medals contenders...
alert(<span class="String"><span class="String">&quot;</span>Gold: <span class="String">&quot;</span></span> <span class="Keyword">+</span> gold)
alert(<span class="String"><span class="String">&quot;</span>Silver: <span class="String">&quot;</span></span> <span class="Keyword">+</span> silver)
alert(<span class="String"><span class="String">&quot;</span>The Field: <span class="String">&quot;</span></span> <span class="Keyword">+</span> the_field)
</pre><pre class="idle"><span class="Storage">var</span> contenders, gold, medalists, silver, the_field;
gold <span class="Keyword">=</span> silver <span class="Keyword">=</span> the_field <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>unknown<span class="String">&quot;</span></span>;
medalists <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">medalists</span>(<span class="FunctionArgument">first, second</span>) {
alert <span class="String"><span class="String">&quot;</span>Gold: <span class="String">&quot;</span></span> <span class="Keyword">+</span> gold
alert <span class="String"><span class="String">&quot;</span>Silver: <span class="String">&quot;</span></span> <span class="Keyword">+</span> silver
alert <span class="String"><span class="String">&quot;</span>The Field: <span class="String">&quot;</span></span> <span class="Keyword">+</span> the_field
</pre><pre class="idle"><span class="Storage">var</span> award_medals, contenders, gold, silver, the_field;
gold <span class="Keyword">=</span> (silver <span class="Keyword">=</span> (the_field <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>unknown<span class="String">&quot;</span></span>));
award_medals <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">award_medals</span>(<span class="FunctionArgument">first, second</span>) {
<span class="Storage">var</span> rest;
rest <span class="Keyword">=</span> <span class="LibraryClassType">Array</span>.<span class="LibraryConstant">prototype</span>.slice.<span class="LibraryFunction">call</span>(arguments, <span class="Number">2</span>);
gold <span class="Keyword">=</span> first;
@@ -622,13 +619,13 @@ medalists <span class="Keyword">=</span> <span class="Storage">function</span> <
<span class="Keyword">return</span> the_field <span class="Keyword">=</span> rest;
};
contenders <span class="Keyword">=</span> [<span class="String"><span class="String">&quot;</span>Michael Phelps<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Liu Xiang<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Yao Ming<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Allyson Felix<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Shawn Johnson<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Roman Sebrle<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Guo Jingjing<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Tyson Gay<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Asafa Powell<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Usain Bolt<span class="String">&quot;</span></span>];
medalists.<span class="LibraryFunction">apply</span>(<span class="Variable">this</span>, contenders);
award_medals.<span class="LibraryFunction">apply</span>(<span class="Variable">this</span>, contenders);
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>Gold: <span class="String">&quot;</span></span> <span class="Keyword">+</span> gold);
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>Silver: <span class="String">&quot;</span></span> <span class="Keyword">+</span> silver);
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>The Field: <span class="String">&quot;</span></span> <span class="Keyword">+</span> the_field);
</pre><button onclick='javascript: var contenders, gold, medalists, silver, the_field;
gold = silver = the_field = "unknown";
medalists = function medalists(first, second) {
</pre><button onclick='javascript: var award_medals, contenders, gold, silver, the_field;
gold = (silver = (the_field = "unknown"));
award_medals = function award_medals(first, second) {
var rest;
rest = Array.prototype.slice.call(arguments, 2);
gold = first;
@@ -636,7 +633,7 @@ medalists = function medalists(first, second) {
return the_field = rest;
};
contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];
medalists.apply(this, contenders);
award_medals.apply(this, contenders);
alert("Gold: " + gold);
alert("Silver: " + silver);
alert("The Field: " + the_field);
@@ -649,39 +646,82 @@ alert("The Field: " + the_field);
<a href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array">Array methods</a>
available.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">backwards</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(arguments.reverse())
<div class='code'><pre class="idle"><span class="FunctionName">backwards</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
alert arguments.reverse()
backwards(<span class="String"><span class="String">&quot;</span>stairway<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>to<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>heaven<span class="String">&quot;</span></span>)
backwards <span class="String"><span class="String">&quot;</span>stairway<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>to<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>heaven<span class="String">&quot;</span></span>
</pre><pre class="idle"><span class="Storage">var</span> backwards;
backwards <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">backwards</span>() {
<span class="Keyword">return</span> <span class="LibraryFunction">alert</span>(<span class="LibraryClassType">Array</span>.<span class="LibraryConstant">prototype</span>.slice.<span class="LibraryFunction">call</span>(arguments, <span class="Number">0</span>).<span class="LibraryFunction">reverse</span>());
<span class="Storage">var</span> arguments <span class="Keyword">=</span> <span class="LibraryClassType">Array</span>.<span class="LibraryConstant">prototype</span>.slice.<span class="LibraryFunction">call</span>(arguments, <span class="Number">0</span>);
<span class="Keyword">return</span> <span class="LibraryFunction">alert</span>(arguments.<span class="LibraryFunction">reverse</span>());
};
backwards(<span class="String"><span class="String">&quot;</span>stairway<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>to<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>heaven<span class="String">&quot;</span></span>);
</pre><button onclick='javascript: var backwards;
backwards = function backwards() {
return alert(Array.prototype.slice.call(arguments, 0).reverse());
var arguments = Array.prototype.slice.call(arguments, 0);
return alert(arguments.reverse());
};
backwards("stairway", "to", "heaven");
;'>run</button><br class='clear' /></div>
<p id="while">
<b class="header">While Loops</b>
The only low-level loop that CoffeeScript provides is the while loop.
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
through the loop.
</p>
<div class='code'><pre class="idle"><span class="Keyword">while</span> demand <span class="Keyword">&gt;</span> supply
sell()
restock()
<div class='code'><pre class="idle"><span class="Comment"><span class="Comment">#</span> Econ 101</span>
<span class="Keyword">if</span> <span class="Variable">this</span>.studying_economics
<span class="Keyword">while</span> supply <span class="Keyword">&gt;</span> demand <span class="Keyword">then</span> buy()
<span class="Keyword">while</span> supply <span class="Keyword">&lt;</span> demand <span class="Keyword">then</span> sell()
<span class="Keyword">while</span> supply <span class="Keyword">&gt;</span> demand <span class="Keyword">then</span> buy()
</pre><pre class="idle"><span class="Keyword">while</span> (demand <span class="Keyword">&gt;</span> supply) {
sell();
restock();
<span class="Comment"><span class="Comment">#</span> Nursery Rhyme</span>
<span class="FunctionName">num</span><span class="Keyword">:</span> <span class="Number">6</span>
<span class="FunctionName">lyrics</span><span class="Keyword">:</span> <span class="Keyword">while</span> num <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">1</span>
num <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> little monkeys, jumping on the bed.</span>
<span class="String"> One fell out and bumped his head.<span class="String">&quot;</span></span>
</pre><pre class="idle"><span class="Storage">var</span> __a, lyrics, num;
<span class="Comment"><span class="Comment">//</span> Econ 101</span>
<span class="Keyword">if</span> (<span class="Variable">this</span>.studying_economics) {
<span class="Keyword">while</span> (supply <span class="Keyword">&gt;</span> demand) {
buy();
}
<span class="Keyword">while</span> (supply <span class="Keyword">&lt;</span> demand) {
sell();
}
}
<span class="Keyword">while</span> (supply <span class="Keyword">&gt;</span> demand) {
buy();
<span class="Comment"><span class="Comment">//</span> Nursery Rhyme</span>
num <span class="Keyword">=</span> <span class="Number">6</span>;
lyrics <span class="Keyword">=</span> (<span class="Storage">function</span>() {
__a <span class="Keyword">=</span> [];
<span class="Keyword">while</span> (num <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">1</span>) {
__a.<span class="LibraryFunction">push</span>(num <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> little monkeys, jumping on the bed. \</span>
<span class="String">One fell out and bumped his head.<span class="String">&quot;</span></span>);
}
<span class="Keyword">return</span> __a;
}).<span class="LibraryFunction">call</span>(<span class="Variable">this</span>);
</pre><button onclick='javascript: var __a, lyrics, num;
// Econ 101
if (this.studying_economics) {
while (supply > demand) {
buy();
}
while (supply < demand) {
sell();
}
}
</pre><br class='clear' /></div>
// Nursery Rhyme
num = 6;
lyrics = (function() {
__a = [];
while (num -= 1) {
__a.push(num + " little monkeys, jumping on the bed. \
One fell out and bumped his head.");
}
return __a;
}).call(this);
;alert(lyrics.join("\n"));'>run: lyrics.join("\n")</button><br class='clear' /></div>
<p>
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
@@ -704,23 +744,23 @@ backwards("stairway", "to", "heaven");
<span class="Comment"><span class="Comment">#</span> Naive collision detection.</span>
<span class="Keyword">for</span> roid <span class="Keyword">in</span> asteroids
<span class="Keyword">for</span> roid2 <span class="Keyword">in</span> asteroids <span class="Keyword">when</span> roid <span class="Keyword">isnt</span> roid2
roid.explode() <span class="Keyword">if</span> roid.overlaps(roid2)
roid.explode() <span class="Keyword">if</span> roid.overlaps roid2
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, __d, __e, __f, __g, food, lunch, roid, roid2;
<span class="Comment"><span class="Comment">//</span> Eat lunch.</span>
lunch <span class="Keyword">=</span> (<span class="Storage">function</span>() {
__a <span class="Keyword">=</span> []; __b <span class="Keyword">=</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>];
<span class="Keyword">for</span> (__c<span class="Keyword">=</span><span class="Number">0</span>; __c<span class="Keyword">&lt;</span>__b.<span class="LibraryConstant">length</span>; __c<span class="Keyword">++</span>) {
<span class="Keyword">for</span> (__c <span class="Keyword">=</span> <span class="Number">0</span>; __c <span class="Keyword">&lt;</span> __b.<span class="LibraryConstant">length</span>; __c<span class="Keyword">++</span>) {
food <span class="Keyword">=</span> __b[__c];
__a.<span class="LibraryFunction">push</span>(eat(food));
}
<span class="Keyword">return</span> __a;
})();
}).<span class="LibraryFunction">call</span>(<span class="Variable">this</span>);
<span class="Comment"><span class="Comment">//</span> Naive collision detection.</span>
__d <span class="Keyword">=</span> asteroids;
<span class="Keyword">for</span> (__e<span class="Keyword">=</span><span class="Number">0</span>; __e<span class="Keyword">&lt;</span>__d.<span class="LibraryConstant">length</span>; __e<span class="Keyword">++</span>) {
<span class="Keyword">for</span> (__e <span class="Keyword">=</span> <span class="Number">0</span>; __e <span class="Keyword">&lt;</span> __d.<span class="LibraryConstant">length</span>; __e<span class="Keyword">++</span>) {
roid <span class="Keyword">=</span> __d[__e];
__f <span class="Keyword">=</span> asteroids;
<span class="Keyword">for</span> (__g<span class="Keyword">=</span><span class="Number">0</span>; __g<span class="Keyword">&lt;</span>__f.<span class="LibraryConstant">length</span>; __g<span class="Keyword">++</span>) {
<span class="Keyword">for</span> (__g <span class="Keyword">=</span> <span class="Number">0</span>; __g <span class="Keyword">&lt;</span> __f.<span class="LibraryConstant">length</span>; __g<span class="Keyword">++</span>) {
roid2 <span class="Keyword">=</span> __f[__g];
<span class="Keyword">if</span> (roid <span class="Keyword">!</span><span class="Keyword">==</span> roid2) {
<span class="Keyword">if</span> (roid.overlaps(roid2)) {
@@ -738,10 +778,10 @@ __d <span class="Keyword">=</span> asteroids;
</p>
<div class='code'><pre class="idle"><span class="FunctionName">countdown</span><span class="Keyword">:</span> num <span class="Keyword">for</span> num <span class="Keyword">in</span> [<span class="Number">10</span>..<span class="Number">1</span>]
<span class="FunctionName">egg_delivery</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
<span class="FunctionName">egg_delivery</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...eggs.length] <span class="Keyword">by</span> <span class="Number">12</span>
<span class="FunctionName">dozen_eggs</span><span class="Keyword">:</span> eggs[i...i<span class="Keyword">+</span><span class="Number">12</span>]
deliver(<span class="Keyword">new</span> <span class="TypeName">egg_carton</span>(dozen))
deliver <span class="Keyword">new</span> <span class="TypeName">egg_carton</span>(dozen)
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, __d, __e, countdown, egg_delivery, num;
countdown <span class="Keyword">=</span> (<span class="Storage">function</span>() {
__a <span class="Keyword">=</span> []; __d <span class="Keyword">=</span> <span class="Number">10</span>; __e <span class="Keyword">=</span> <span class="Number">1</span>;
@@ -749,7 +789,7 @@ countdown <span class="Keyword">=</span> (<span class="Storage">function</span>(
__a.<span class="LibraryFunction">push</span>(num);
}
<span class="Keyword">return</span> __a;
})();
}).<span class="LibraryFunction">call</span>(<span class="Variable">this</span>);
egg_delivery <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">egg_delivery</span>() {
<span class="Storage">var</span> __f, __g, __h, __i, __j, dozen_eggs, i;
__f <span class="Keyword">=</span> []; __i <span class="Keyword">=</span> <span class="Number">0</span>; __j <span class="Keyword">=</span> eggs.<span class="LibraryConstant">length</span>;
@@ -757,7 +797,7 @@ egg_delivery <span class="Keyword">=</span> <span class="Storage">function</span
__f.<span class="LibraryFunction">push</span>((<span class="Storage">function</span>() {
dozen_eggs <span class="Keyword">=</span> eggs.<span class="LibraryFunction">slice</span>(i, i <span class="Keyword">+</span> <span class="Number">12</span>);
<span class="Keyword">return</span> deliver(<span class="Keyword">new</span> <span class="TypeName">egg_carton</span>(dozen));
})());
}).<span class="LibraryFunction">call</span>(<span class="Variable">this</span>));
}
<span class="Keyword">return</span> __f;
};
@@ -768,7 +808,7 @@ countdown = (function() {
__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;
@@ -776,7 +816,7 @@ egg_delivery = function egg_delivery() {
__f.push((function() {
dozen_eggs = eggs.slice(i, i + 12);
return deliver(new egg_carton(dozen));
})());
}).call(this));
}
return __f;
};
@@ -791,6 +831,7 @@ egg_delivery = function egg_delivery() {
<span class="FunctionName">ages</span><span class="Keyword">:</span> <span class="Keyword">for</span> child, age <span class="Keyword">of</span> years_old
child <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> is <span class="String">&quot;</span></span> <span class="Keyword">+</span> age
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, age, ages, child, years_old;
<span class="Storage">var</span> __hasProp <span class="Keyword">=</span> <span class="LibraryClassType">Object</span>.<span class="LibraryConstant">prototype</span>.hasOwnProperty;
years_old <span class="Keyword">=</span> {
max: <span class="Number">10</span>,
ida: <span class="Number">9</span>,
@@ -800,13 +841,14 @@ ages <span class="Keyword">=</span> (<span class="Storage">function</span>() {
__a <span class="Keyword">=</span> []; __b <span class="Keyword">=</span> years_old;
<span class="Keyword">for</span> (child <span class="Keyword">in</span> __b) {
age <span class="Keyword">=</span> __b[child];
<span class="Keyword">if</span> (__b.hasOwnProperty(child)) {
<span class="Keyword">if</span> (__hasProp.<span class="LibraryFunction">call</span>(__b, child)) {
__a.<span class="LibraryFunction">push</span>(child <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> is <span class="String">&quot;</span></span> <span class="Keyword">+</span> age);
}
}
<span class="Keyword">return</span> __a;
})();
}).<span class="LibraryFunction">call</span>(<span class="Variable">this</span>);
</pre><button onclick='javascript: var __a, __b, age, ages, child, years_old;
var __hasProp = Object.prototype.hasOwnProperty;
years_old = {
max: 10,
ida: 9,
@@ -816,12 +858,12 @@ ages = (function() {
__a = []; __b = years_old;
for (child in __b) {
age = __b[child];
if (__b.hasOwnProperty(child)) {
if (__hasProp.call(__b, child)) {
__a.push(child + " is " + age);
}
}
return __a;
})();
}).call(this);
;alert(ages.join(", "));'>run: ages.join(", ")</button><br class='clear' /></div>
<p id="slice_splice">
@@ -874,7 +916,7 @@ numbers.splice.apply(numbers, [3, 6 - 3 + 1].concat([-3, -4, -5, -6]));
pushed down into each possible branch of execution, in the function
below.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">grade</span><span class="Keyword">:</span> <span class="FunctionArgument">student</span> <span class="Storage">=&gt;</span>
<div class='code'><pre class="idle"><span class="FunctionName">grade</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">student</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="Keyword">if</span> student.excellent_work
<span class="String"><span class="String">&quot;</span>A+<span class="String">&quot;</span></span>
<span class="Keyword">else</span> <span class="Keyword">if</span> student.okay_stuff
@@ -917,9 +959,9 @@ eldest = 24 > 21 ? "Liz" : "Ike";
</p>
<div class='code'><pre class="idle"><span class="FunctionName">six</span><span class="Keyword">:</span> (<span class="FunctionName">one</span><span class="Keyword">:</span> <span class="Number">1</span>) <span class="Keyword">+</span> (<span class="FunctionName">two</span><span class="Keyword">:</span> <span class="Number">2</span>) <span class="Keyword">+</span> (<span class="FunctionName">three</span><span class="Keyword">:</span> <span class="Number">3</span>)
</pre><pre class="idle"><span class="Storage">var</span> one, six, three, two;
six <span class="Keyword">=</span> (one <span class="Keyword">=</span> <span class="Number">1</span>) <span class="Keyword">+</span> (two <span class="Keyword">=</span> <span class="Number">2</span>) <span class="Keyword">+</span> (three <span class="Keyword">=</span> <span class="Number">3</span>);
six <span class="Keyword">=</span> ((one <span class="Keyword">=</span> <span class="Number">1</span>)) <span class="Keyword">+</span> ((two <span class="Keyword">=</span> <span class="Number">2</span>)) <span class="Keyword">+</span> ((three <span class="Keyword">=</span> <span class="Number">3</span>));
</pre><button onclick='javascript: var one, six, three, two;
six = (one = 1) + (two = 2) + (three = 3);
six = ((one = 1)) + ((two = 2)) + ((three = 3));
;alert(six);'>run: six</button><br class='clear' /></div>
<p>
Things that would otherwise be statements in JavaScript, when used
@@ -931,27 +973,29 @@ six = (one = 1) + (two = 2) + (three = 3);
<span class="FunctionName">globals</span><span class="Keyword">:</span> (name <span class="Keyword">for</span> name <span class="Keyword">of</span> window)[<span class="Number">0</span>...<span class="Number">10</span>]
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, globals, name;
<span class="Storage">var</span> __hasProp <span class="Keyword">=</span> <span class="LibraryClassType">Object</span>.<span class="LibraryConstant">prototype</span>.hasOwnProperty;
<span class="Comment"><span class="Comment">//</span> The first ten global properties.</span>
globals <span class="Keyword">=</span> ((<span class="Storage">function</span>() {
__a <span class="Keyword">=</span> []; __b <span class="Keyword">=</span> <span class="LibraryClassType">window</span>;
<span class="Keyword">for</span> (name <span class="Keyword">in</span> __b) {
<span class="Keyword">if</span> (__b.hasOwnProperty(name)) {
<span class="Keyword">if</span> (__hasProp.<span class="LibraryFunction">call</span>(__b, name)) {
__a.<span class="LibraryFunction">push</span>(name);
}
}
<span class="Keyword">return</span> __a;
})()).<span class="LibraryFunction">slice</span>(<span class="Number">0</span>, <span class="Number">10</span>);
}).<span class="LibraryFunction">call</span>(<span class="Variable">this</span>)).<span class="LibraryFunction">slice</span>(<span class="Number">0</span>, <span class="Number">10</span>);
</pre><button onclick='javascript: var __a, __b, globals, name;
var __hasProp = Object.prototype.hasOwnProperty;
// The first ten global properties.
globals = ((function() {
__a = []; __b = window;
for (name in __b) {
if (__b.hasOwnProperty(name)) {
if (__hasProp.call(__b, name)) {
__a.push(name);
}
}
return __a;
})()).slice(0, 10);
}).call(this)).slice(0, 10);
;alert(globals);'>run: globals</button><br class='clear' /></div>
<p>
As well as silly things, like passing a <b>try/catch</b> statement directly
@@ -961,23 +1005,79 @@ globals = ((function() {
<span class="Keyword">try</span>
nonexistent <span class="Keyword">/</span> <span class="BuiltInConstant">undefined</span>
<span class="Keyword">catch</span> error
<span class="String"><span class="String">&quot;</span>Caught an error: <span class="String">&quot;</span></span> <span class="Keyword">+</span> error
<span class="String"><span class="String">&quot;</span>And the error is ... <span class="String">&quot;</span></span> <span class="Keyword">+</span> error
)
</pre><pre class="idle"><span class="LibraryFunction">alert</span>((<span class="Storage">function</span>() {
<span class="Keyword">try</span> {
<span class="Keyword">return</span> nonexistent / undefined;
} <span class="Keyword">catch</span> (error) {
<span class="Keyword">return</span> <span class="String"><span class="String">&quot;</span>Caught an error: <span class="String">&quot;</span></span> <span class="Keyword">+</span> error;
<span class="Keyword">return</span> <span class="String"><span class="String">&quot;</span>And the error is ... <span class="String">&quot;</span></span> <span class="Keyword">+</span> error;
}
})());
}).<span class="LibraryFunction">call</span>(<span class="Variable">this</span>));
</pre><button onclick='javascript: alert((function() {
try {
return nonexistent / undefined;
} catch (error) {
return "Caught an error: " + error;
return "And the error is ... " + error;
}
})());
}).call(this));
;'>run</button><br class='clear' /></div>
<p>
There are a handful of statements in JavaScript that can't be meaningfully
converted into expressions, namely <tt>break</tt>, <tt>continue</tt>,
and <tt>return</tt>. If you make use of them within a block of code,
CoffeeScript won't try to perform the conversion.
</p>
<p id="existence">
<b class="header">The Existential Operator</b>
It's a little difficult to check for the existence of a variable in
JavaScript. <tt>if (variable) ...</tt> comes close, but fails for zero,
the empty string, and false. CoffeeScript's existential operator <tt>?</tt> returns true unless
a variable is <b>null</b> or <b>undefined</b>, which makes it analogous
to Ruby's <tt>nil?</tt>
</p>
<p>
It can also be used for safer conditional assignment than <tt>||=</tt>
provides, for cases where you may be handling numbers or strings.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">solipsism</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> mind<span class="Keyword">?</span> <span class="Keyword">and</span> <span class="Keyword">not</span> world<span class="Keyword">?</span>
speed <span class="Keyword">?</span><span class="Keyword">=</span> <span class="Number">140</span>
</pre><pre class="idle"><span class="Storage">var</span> solipsism, speed;
<span class="Keyword">if</span> ((<span class="Keyword">typeof</span> mind <span class="Keyword">!</span><span class="Keyword">==</span> <span class="String"><span class="String">&quot;</span>undefined<span class="String">&quot;</span></span> <span class="Keyword">&amp;</span><span class="Keyword">&amp;</span> mind <span class="Keyword">!</span><span class="Keyword">==</span> <span class="BuiltInConstant">null</span>) <span class="Keyword">&amp;</span><span class="Keyword">&amp;</span> <span class="Keyword">!</span>(<span class="Keyword">typeof</span> world <span class="Keyword">!</span><span class="Keyword">==</span> <span class="String"><span class="String">&quot;</span>undefined<span class="String">&quot;</span></span> <span class="Keyword">&amp;</span><span class="Keyword">&amp;</span> world <span class="Keyword">!</span><span class="Keyword">==</span> <span class="BuiltInConstant">null</span>)) {
solipsism <span class="Keyword">=</span> <span class="BuiltInConstant">true</span>;
}
speed <span class="Keyword">=</span> (<span class="Keyword">typeof</span> speed <span class="Keyword">!</span><span class="Keyword">==</span> <span class="String"><span class="String">&quot;</span>undefined<span class="String">&quot;</span></span> <span class="Keyword">&amp;</span><span class="Keyword">&amp;</span> speed <span class="Keyword">!</span><span class="Keyword">==</span> <span class="BuiltInConstant">null</span>) ? speed : <span class="Number">140</span>;
</pre><button onclick='javascript: var solipsism, speed;
if ((typeof mind !== "undefined" && mind !== null) && !(typeof world !== "undefined" && world !== null)) {
solipsism = true;
}
speed = (typeof speed !== "undefined" && speed !== null) ? speed : 140;
;alert(speed);'>run: speed</button><br class='clear' /></div>
<p>
The accessor variant of the existential operator <tt>?.</tt> can be used to soak
up null references in a chain of properties. Use it instead
of the dot accessor <tt>.</tt> in cases where the base value may be <b>null</b>
or <b>undefined</b>. If all of the properties exist then you'll get the expected
result, if the chain is broken, <b>undefined</b> is returned instead of
the <b>TypeError</b> that would be raised otherwise.
</p>
<div class='code'><pre class="idle">lottery.draw_winner()<span class="Keyword">?</span>.address<span class="Keyword">?</span>.zipcode
</pre><pre class="idle"><span class="Storage">var</span> __a;
((__a <span class="Keyword">=</span> lottery.draw_winner()) <span class="Keyword">==</span> undefined ? undefined : __a.address <span class="Keyword">==</span> undefined ? undefined : __a.address.zipcode);
</pre><br class='clear' /></div>
<p>
Soaking up nulls is similar to Ruby's
<a href="http://andand.rubyforge.org/">andand gem</a>, and to the
<a href="http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator%28%3F.%29">safe navigation operator</a>
in Groovy.
</p>
<p id="inheritance">
<b class="header">Inheritance, and Calling Super from a Subclass</b>
@@ -999,24 +1099,24 @@ globals = ((function() {
object's prototype, and converts <tt>super()</tt> into a call against
the immediate ancestor's method of the same name.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">Animal</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
<span class="FunctionName">Animal::move</span><span class="Keyword">:</span> <span class="FunctionArgument">meters</span> <span class="Storage">=&gt;</span>
alert(<span class="Variable">this</span>.name <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>)
<div class='code'><pre class="idle"><span class="FunctionName">Animal</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
<span class="FunctionName">Animal::move</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">meters</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
alert <span class="Variable">this</span>.name <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>
<span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</span> <span class="FunctionName">this.name</span><span class="Keyword">:</span> name
<span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">name</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="FunctionName">this.name</span><span class="Keyword">:</span> name
Snake <span class="Variable">extends</span> Animal
<span class="FunctionName">Snake::move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(<span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>)
<span class="Variable">super</span>(<span class="Number">5</span>)
<span class="FunctionName">Snake::move</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
alert <span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>
<span class="Variable">super</span> <span class="Number">5</span>
<span class="FunctionName">Horse</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</span> <span class="FunctionName">this.name</span><span class="Keyword">:</span> name
<span class="FunctionName">Horse</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">name</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span> <span class="FunctionName">this.name</span><span class="Keyword">:</span> name
Horse <span class="Variable">extends</span> Animal
<span class="FunctionName">Horse::move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(<span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>)
<span class="Variable">super</span>(<span class="Number">45</span>)
<span class="FunctionName">Horse::move</span><span class="Keyword">:</span> <span class="Storage">-&gt;</span>
alert <span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>
<span class="Variable">super</span> <span class="Number">45</span>
<span class="FunctionName">sam</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Snake</span>(<span class="String"><span class="String">&quot;</span>Sammy the Python<span class="String">&quot;</span></span>)
<span class="FunctionName">tom</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Horse</span>(<span class="String"><span class="String">&quot;</span>Tommy the Palomino<span class="String">&quot;</span></span>)
<span class="FunctionName">sam</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Snake</span> <span class="String"><span class="String">&quot;</span>Sammy the Python<span class="String">&quot;</span></span>
<span class="FunctionName">tom</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Horse</span> <span class="String"><span class="String">&quot;</span>Tommy the Palomino<span class="String">&quot;</span></span>
sam.move()
tom.move()
@@ -1025,8 +1125,7 @@ tom.move()
</pre><pre class="idle"><span class="Storage">var</span> Animal, Horse, Snake, __a, __b, sam, tom;
Animal <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">Animal</span>() {
};
Animal <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">Animal</span>() { };
<span class="LibraryClassType">Animal</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">move</span> = <span class="Storage">function</span> <span class="FunctionName">move</span>(<span class="FunctionArgument">meters</span>) {
<span class="Keyword">return</span> <span class="LibraryFunction">alert</span>(<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>);
};
@@ -1063,8 +1162,7 @@ tom <span class="Keyword">=</span> <span class="Keyword">new</span> <span class=
sam.move();
tom.move();
</pre><button onclick='javascript: var Animal, Horse, Snake, __a, __b, sam, tom;
Animal = function Animal() {
};
Animal = function Animal() { };
Animal.prototype.move = function move(meters) {
return alert(this.name + " moved " + meters + "m.");
};
@@ -1102,30 +1200,6 @@ sam.move();
tom.move();
;'>run</button><br class='clear' /></div>
<p id="blocks">
<b class="header">Blocks</b>
Many common looping functions (in Prototype, jQuery, and Underscore,
for example) take a single function as their final argument. To make
final functions easier to pass, CoffeeScript includes block syntax,
so you don't have to close the parentheses on the other side.
</p>
<div class='code'><pre class="idle">$(<span class="String"><span class="String">'</span>table.list<span class="String">'</span></span>).each()<span class="FunctionArgument"> table </span><span class="Storage">=&gt;</span>
$(<span class="String"><span class="String">'</span>tr.account<span class="String">'</span></span>, table).each()<span class="FunctionArgument"> row </span><span class="Storage">=&gt;</span>
row.show()
row.highlight()
</pre><pre class="idle"><span class="Keyword">$</span>(<span class="String"><span class="String">'</span>table.list<span class="String">'</span></span>).each(<span class="Storage">function</span>(table) {
<span class="Keyword">return</span> <span class="Keyword">$</span>(<span class="String"><span class="String">'</span>tr.account<span class="String">'</span></span>, table).each(<span class="Storage">function</span>(row) {
row.show();
<span class="Keyword">return</span> row.highlight();
});
});
</pre><br class='clear' /></div>
<p>
If you prefer not to use blocks, you'll need to add a pair of parentheses
to help distinguish the arguments from the definition of the function:
<tt>_.map(array, (num => num * 2))</tt>
</p>
<p id="pattern_matching">
<b class="header">Pattern Matching (Destructuring Assignment)</b>
To make extracting values from complex arrays and objects more convenient,
@@ -1157,11 +1231,11 @@ and_switch = __a[1];
But it's also helpful for dealing with functions that return multiple
values.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">weather_report</span><span class="Keyword">:</span> <span class="FunctionArgument">location</span> <span class="Storage">=&gt;</span>
<div class='code'><pre class="idle"><span class="FunctionName">weather_report</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">location</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="Comment"><span class="Comment">#</span> Make an Ajax request to fetch the weather...</span>
[location, <span class="Number">72</span>, <span class="String"><span class="String">&quot;</span>Mostly Sunny<span class="String">&quot;</span></span>]
[city, temp, forecast]<span class="Keyword">:</span> weather_report(<span class="String"><span class="String">&quot;</span>Berkeley, CA<span class="String">&quot;</span></span>)
[city, temp, forecast]<span class="Keyword">:</span> weather_report <span class="String"><span class="String">&quot;</span>Berkeley, CA<span class="String">&quot;</span></span>
</pre><pre class="idle"><span class="Storage">var</span> __a, city, forecast, temp, weather_report;
weather_report <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">weather_report</span>(<span class="FunctionArgument">location</span>) {
<span class="Comment"><span class="Comment">//</span> Make an Ajax request to fetch the weather...</span>
@@ -1230,6 +1304,38 @@ street = __c[0];
city = __c[1];
;alert(poet + " — " + street);'>run: poet + " — " + street</button><br class='clear' /></div>
<p id="fat_arrow">
<b class="header">Function binding</b>
The fat arrow <tt>=></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
to use with <tt>bind</tt>. Functions created with the fat arrow are able to access
properties of the <tt>this</tt> where they're defined.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">Account</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">customer, cart</span><span class="FunctionArgument">)</span> <span class="Storage">-&gt;</span>
<span class="FunctionName">this.customer</span><span class="Keyword">:</span> customer
<span class="FunctionName">this.cart</span><span class="Keyword">:</span> cart
$(<span class="String"><span class="String">'</span>.shopping_cart<span class="String">'</span></span>).bind <span class="String"><span class="String">'</span>click<span class="String">'</span></span>, <span class="FunctionArgument">(</span><span class="FunctionArgument">event</span><span class="FunctionArgument">)</span> <span class="Storage">=&gt;</span>
<span class="Variable">this</span>.customer.purchase <span class="Variable">this</span>.cart
</pre><pre class="idle"><span class="Storage">var</span> Account;
Account <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">Account</span>(<span class="FunctionArgument">customer, cart</span>) {
<span class="Storage">var</span> __a;
<span class="Variable">this</span>.customer <span class="Keyword">=</span> customer;
<span class="Variable">this</span>.cart <span class="Keyword">=</span> cart;
__a <span class="Keyword">=</span> <span class="Keyword">$</span>(<span class="String"><span class="String">'</span>.shopping_cart<span class="String">'</span></span>).bind(<span class="String"><span class="String">'</span>click<span class="String">'</span></span>, (<span class="Storage">function</span>(__this) {
<span class="Storage">var</span> <span class="FunctionName">__func</span> = <span class="Storage">function</span>(<span class="FunctionArgument">event</span>) {
<span class="Keyword">return</span> <span class="Variable">this</span>.customer.purchase(<span class="Variable">this</span>.cart);
};
<span class="Keyword">return</span> (<span class="Storage">function</span>() {
<span class="Keyword">return</span> __func.<span class="LibraryFunction">apply</span>(__this, arguments);
});
})(<span class="Variable">this</span>));
<span class="Keyword">return</span> Account <span class="Keyword">===</span> <span class="Variable">this</span>.<span class="LibraryConstant">constructor</span> ? <span class="Variable">this</span> : __a;
};
</pre><br class='clear' /></div>
<p id="embedded">
<b class="header">Embedded JavaScript</b>
Hopefully, you'll never need to use it, but if you ever need to intersperse
@@ -1261,25 +1367,33 @@ return [document.title, "Hello JavaScript"].join(": ");
in a returnable, assignable expression. The format is: <tt>switch</tt> condition,
<tt>when</tt> clauses, <tt>else</tt> the default case.
</p>
<p>
As in Ruby, <b>switch</b> statements in CoffeeScript can take multiple
values for each <b>when</b> clause. If any of the values match, the clause
runs.
</p>
<div class='code'><pre class="idle"><span class="Keyword">switch</span> day
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Tuesday<span class="String">&quot;</span></span> <span class="Keyword">then</span> eat_breakfast()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Wednesday<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_the_park()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Saturday<span class="String">&quot;</span></span>
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Mon<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_work()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Tue<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_the_park()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Thu<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_ice_fishing()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Fri<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Sat<span class="String">&quot;</span></span>
<span class="Keyword">if</span> day <span class="Keyword">is</span> bingo_day
go_to_bingo()
go_dancing()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Sunday<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_church()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Sun<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_church()
<span class="Keyword">else</span> go_to_work()
</pre><pre class="idle"><span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Tuesday<span class="String">&quot;</span></span>) {
eat_breakfast();
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Wednesday<span class="String">&quot;</span></span>) {
</pre><pre class="idle"><span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Mon<span class="String">&quot;</span></span>) {
go_to_work();
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Tue<span class="String">&quot;</span></span>) {
go_to_the_park();
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Saturday<span class="String">&quot;</span></span>) {
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Thu<span class="String">&quot;</span></span>) {
go_ice_fishing();
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Fri<span class="String">&quot;</span></span> <span class="Keyword">||</span> day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Sat<span class="String">&quot;</span></span>) {
<span class="Keyword">if</span> (day <span class="Keyword">===</span> bingo_day) {
go_to_bingo();
go_dancing();
}
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Sunday<span class="String">&quot;</span></span>) {
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Sun<span class="String">&quot;</span></span>) {
go_to_church();
} <span class="Keyword">else</span> {
go_to_work();
@@ -1295,7 +1409,7 @@ return [document.title, "Hello JavaScript"].join(": ");
all_hell_breaks_loose()
cats_and_dogs_living_together()
<span class="Keyword">catch</span> error
print(error)
print error
<span class="Keyword">finally</span>
clean_up()
</pre><pre class="idle"><span class="Keyword">try</span> {
@@ -1308,6 +1422,26 @@ return [document.title, "Hello JavaScript"].join(": ");
}
</pre><br class='clear' /></div>
<p id="comparisons">
<b class="header">Chained Comparisons</b>
CoffeeScript borrows
<a href="http://docs.python.org/reference/expressions.html#notin">chained comparisons</a>
from Python &mdash; making it easy to test if a value falls within a
certain range.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">cholesterol</span><span class="Keyword">:</span> <span class="Number">127</span>
<span class="FunctionName">healthy</span><span class="Keyword">:</span> <span class="Number">200</span> <span class="Keyword">&gt;</span> cholesterol <span class="Keyword">&gt;</span> <span class="Number">60</span>
</pre><pre class="idle"><span class="Storage">var</span> cholesterol, healthy;
cholesterol <span class="Keyword">=</span> <span class="Number">127</span>;
healthy <span class="Keyword">=</span> (<span class="Number">200</span> <span class="Keyword">&gt;</span> cholesterol) <span class="Keyword">&amp;</span><span class="Keyword">&amp;</span> (cholesterol <span class="Keyword">&gt;</span> <span class="Number">60</span>);
</pre><button onclick='javascript: var cholesterol, healthy;
cholesterol = 127;
healthy = (200 > cholesterol) && (cholesterol > 60);
;alert(healthy);'>run: healthy</button><br class='clear' /></div>
<p id="strings">
<b class="header">Multiline Strings and Heredocs</b>
Multiline strings are allowed in CoffeeScript.
@@ -1341,11 +1475,11 @@ world...";
indentation level that begins the heredoc is maintained throughout, so
you can keep it all aligned with the body of your code.
</p>
<div class='code'><pre class="idle"><span class="FunctionName">html</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span><span class="String">'</span></span><span class="String"><span class="String">'</span></span>
<div class='code'><pre class="idle"><span class="FunctionName">html</span><span class="Keyword">:</span> <span class="String"><span class="String">'''</span></span>
<span class="String"> &lt;strong&gt;</span>
<span class="String"> cup of coffeescript</span>
<span class="String"> &lt;/strong&gt;</span>
<span class="String"> <span class="String">'</span></span><span class="String"><span class="String">'</span><span class="String">'</span></span>
<span class="String"> <span class="String">'''</span></span>
</pre><pre class="idle"><span class="Storage">var</span> html;
html <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>&lt;strong&gt;<span class="UserDefinedConstant">\n</span> cup of coffeescript<span class="UserDefinedConstant">\n</span>&lt;/strong&gt;<span class="String">&quot;</span></span>;
</pre><br class='clear' /></div>
@@ -1365,6 +1499,12 @@ html <span class="Keyword">=</span> <span class="String"><span class="String">&q
<li>
<a href="http://github.com/jashkenas/coffee-script/issues">Bugs, Feature Requests, and General Discussion</a>
</li>
<li>
<a href="http://github.com/creationix/coffeepot">CoffeePot</a><br />
An implementation of CoffeeScript, written in CoffeeScript, by
<a href="http://github.com/creationix">Tim Caswell</a>. Compiles just
a limited subset at this point.
</li>
<li>
<a href="http://github.com/jnicklas/bistro_car">BistroCar</a><br />
A Rails plugin by
@@ -1372,41 +1512,49 @@ html <span class="Keyword">=</span> <span class="String"><span class="String">&q
that includes CoffeeScript helpers,
bundling and minification.
</li>
</ul>
<h2 id="contributing">Contributing</h2>
<p>
Here's a wish list of things that would be wonderful to have contributed:
</p>
<ul>
<li>
<a href="http://github.com/jashkenas/coffee-script/issues#issue/8">
A CoffeeScript version of the compiler.
</a>
</li>
<li>
Test cases for any syntax errors you encounter that you think CoffeeScript
should be able to compile properly.
</li>
<li>
A tutorial that introduces CoffeeScript from the ground up for folks
without knowledge of JavaScript.
</li>
<li>
Integration with Processing.js's JavaScript API (this would depend on
having a JavaScript version of the compiler).
</li>
<li>
A lot of the code generation in <tt>nodes.rb</tt> gets into messy
string manipulation. Techniques for cleaning this up across the board
would be appreciated.
<a href="http://github.com/inem/coffee-haml-filter">coffee-haml-filter</a><br />
A custom <a href="http://haml-lang.com/">HAML</a> filter, by
<a href="http://github.com/inem">Ivan Nemytchenko</a>, that embeds
snippets of CoffeeScript within your HAML templates.
</li>
</ul>
<h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 20px;">0.3.0</b>
CoffeeScript 0.3 includes major syntax changes:
<br />
The function symbol was changed to
<tt>-></tt>, and the bound function symbol is now <tt>=></tt>.
<br />
Parameter lists in function definitions must now be wrapped in parentheses.
<br />
Added property soaking, with the <tt>?.</tt> operator.
<br />
Made parentheses optional, when invoking functions with arguments.
<br />
Removed the obsolete block literal syntax.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.2.6</b>
Added Python-style chained comparisons, the conditional existence
operator <tt>?=</tt>, and some examples from <i>Beautiful Code</i>.
Bugfixes relating to statement-to-expression conversion, arguments-to-array
conversion, and the TextMate syntax highlighter.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.2.5</b>
The conditions in switch statements can now take multiple values at once &mdash;
If any of them are true, the case will run. Added the long arrow <tt>==></tt>,
which defines and immediately binds a function to <tt>this</tt>. While loops can
now be used as expressions, in the same way that comprehensions can. Splats
can be used within pattern matches to soak up the rest of an array.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.2.4</b>
Added ECMAScript Harmony style destructuring assignment, for dealing with
@@ -1450,7 +1598,7 @@ html <span class="Keyword">=</span> <span class="String"><span class="String">&q
<b class="header" style="margin-top: 20px;">0.2.0</b>
Major release. Significant whitespace. Better statement-to-expression
conversion. Splats. Splice literals. Object comprehensions. Blocks.
The existence operator. Many thanks to all the folks who posted issues,
The existential operator. Many thanks to all the folks who posted issues,
with special thanks to
<a href="http://github.com/kamatsu">Liam O'Connor-Davis</a> for whitespace
and expression help.

View File

@@ -10,7 +10,7 @@ require "coffee_script/parse_error"
# Namespace for all CoffeeScript internal classes.
module CoffeeScript
VERSION = '0.2.4' # Keep in sync with the gemspec.
VERSION = '0.3.0' # Keep in sync with the gemspec.
# Compile a script (String or IO) to JavaScript.
def self.compile(script, options={})

View File

@@ -25,9 +25,11 @@ Usage:
# Seconds to pause between checks for changed source files.
WATCH_INTERVAL = 0.5
# Path to the root of the CoffeeScript install.
ROOT = File.expand_path(File.dirname(__FILE__) + '/../..')
# Command to execute in Narwhal
PACKAGE = File.expand_path(File.dirname(__FILE__) + '/../..')
LAUNCHER = "narwhal -p #{PACKAGE} -e 'require(\"coffee-script\").run(system.args);'"
LAUNCHER = "narwhal -p #{ROOT} -e 'require(\"coffee-script\").run(system.args);'"
# Run the CommandLine off the contents of ARGV.
def initialize
@@ -141,7 +143,7 @@ Usage:
options[:no_wrap] = true if @options[:no_wrap]
options[:globals] = true if @options[:globals]
CoffeeScript.compile(script, options)
rescue CoffeeScript::ParseError, SyntaxError => e
rescue CoffeeScript::ParseError => e
STDERR.puts "#{source}: #{e.message}"
exit(1) unless @options[:watch]
nil
@@ -159,7 +161,7 @@ Usage:
# Install the CoffeeScript TextMate bundle to ~/Library.
def install_bundle
bundle_dir = File.expand_path('~/Library/Application Support/TextMate/Bundles/')
FileUtils.cp_r(File.dirname(__FILE__) + '/CoffeeScript.tmbundle', bundle_dir)
FileUtils.cp_r("#{ROOT}/extras/CoffeeScript.tmbundle", bundle_dir)
end
# Use OptionParser for all the options.

View File

@@ -4,8 +4,9 @@ class Parser
token IF ELSE UNLESS
token NUMBER STRING REGEX
token TRUE FALSE YES NO ON OFF
token IDENTIFIER PROPERTY_ACCESS PROTOTYPE_ACCESS
token CODE PARAM NEW RETURN
token IDENTIFIER PROPERTY_ACCESS PROTOTYPE_ACCESS SOAK_ACCESS
token CODE PARAM_START PARAM PARAM_END NEW RETURN
token CALL_START CALL_END INDEX_START INDEX_END
token TRY CATCH FINALLY THROW
token BREAK CONTINUE
token FOR IN OF BY WHEN WHILE
@@ -21,7 +22,7 @@ token INDENT OUTDENT
# Declare order of operations.
prechigh
left '?'
nonassoc UMINUS NOT '!' '!!' '~' '++' '--'
nonassoc UMINUS UPLUS NOT '!' '!!' '~' '++' '--'
left '*' '/' '%'
left '+' '-'
left '<<' '>>' '>>>'
@@ -37,9 +38,9 @@ prechigh
right WHEN LEADING_WHEN IN OF BY
right THROW FOR NEW SUPER
left EXTENDS
left ASSIGN '||=' '&&='
right RETURN
right '=>' UNLESS IF ELSE WHILE
left '||=' '&&=' '?='
right ASSIGN RETURN
right '->' '=>' UNLESS IF ELSE WHILE
preclow
rule
@@ -102,12 +103,12 @@ rule
| BREAK { result = LiteralNode.new(val[0]) }
| CONTINUE { result = LiteralNode.new(val[0]) }
| ARGUMENTS { result = LiteralNode.new(val[0]) }
| TRUE { result = LiteralNode.new(true) }
| FALSE { result = LiteralNode.new(false) }
| YES { result = LiteralNode.new(true) }
| NO { result = LiteralNode.new(false) }
| ON { result = LiteralNode.new(true) }
| OFF { result = LiteralNode.new(false) }
| TRUE { result = LiteralNode.new(Value.new(true)) }
| FALSE { result = LiteralNode.new(Value.new(false)) }
| YES { result = LiteralNode.new(Value.new(true)) }
| NO { result = LiteralNode.new(Value.new(false)) }
| ON { result = LiteralNode.new(Value.new(true)) }
| OFF { result = LiteralNode.new(Value.new(false)) }
;
# Assignment to a variable (or index).
@@ -140,6 +141,7 @@ rule
'!' Expression { result = OpNode.new(val[0], val[1]) }
| '!!' Expression { result = OpNode.new(val[0], val[1]) }
| '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
| '+' Expression = UPLUS { result = OpNode.new(val[0], val[1]) }
| NOT Expression { result = OpNode.new(val[0], val[1]) }
| '~' Expression { result = OpNode.new(val[0], val[1]) }
| '--' Expression { result = OpNode.new(val[0], val[1]) }
@@ -178,6 +180,7 @@ rule
| Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '?' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
@@ -186,6 +189,7 @@ rule
| Expression '%=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '?=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression INSTANCEOF Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression IN Expression { result = OpNode.new(val[1], val[0], val[2]) }
@@ -198,8 +202,15 @@ rule
# Function definition.
Code:
ParamList "=>" Block { result = CodeNode.new(val[0], val[2]) }
| "=>" Block { result = CodeNode.new([], val[1]) }
PARAM_START ParamList PARAM_END
FuncGlyph Block { result = CodeNode.new(val[1], val[4], val[3]) }
| FuncGlyph Block { result = CodeNode.new([], val[1], val[0]) }
;
# The symbols to signify functions, and bound functions.
FuncGlyph:
'->' { result = :func }
| '=>' { result = :boundfunc }
;
# The parameters to a function definition.
@@ -211,12 +222,12 @@ rule
# A Parameter (or ParamSplat) in a function definition.
Param:
PARAM
| PARAM "." "." "." { result = ParamSplatNode.new(val[0]) }
| PARAM "." "." "." { result = SplatNode.new(val[0]) }
;
# A regular splat.
Splat:
Expression "." "." "." { result = ArgSplatNode.new(val[0])}
Expression "." "." "." { result = SplatNode.new(val[0]) }
;
# Expressions that can be treated as values.
@@ -234,14 +245,15 @@ rule
# Accessing into an object or array, through dot or index notation.
Accessor:
PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
| PROTOTYPE_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], true) }
| PROTOTYPE_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], :prototype) }
| SOAK_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], :soak) }
| Index { result = val[0] }
| Range { result = SliceNode.new(val[0]) }
| Slice { result = SliceNode.new(val[0]) }
;
# Indexing into an object or array.
Index:
"[" Expression "]" { result = IndexNode.new(val[1]) }
INDEX_START Expression INDEX_END { result = IndexNode.new(val[1]) }
;
# An object literal.
@@ -276,18 +288,16 @@ rule
Invocation:
Value Arguments { result = CallNode.new(val[0], val[1]) }
| Invocation Arguments { result = CallNode.new(val[0], val[1]) }
# | Invocation Code { result = val[0] << val[1] }
;
# The list of arguments to a function invocation.
Arguments:
"(" ArgList ")" { result = val[1] }
| "(" ArgList ")" Code { result = val[1] << val[3] }
CALL_START ArgList CALL_END { result = val[1] }
;
# Calling super.
Super:
SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
SUPER CALL_START ArgList CALL_END { result = CallNode.new(Value.new('super'), val[2]) }
;
# The range literal.
@@ -298,6 +308,14 @@ rule
"." "." "." Expression "]" { result = RangeNode.new(val[1], val[5], true) }
;
# The slice literal.
Slice:
INDEX_START Expression "." "."
Expression INDEX_END { result = RangeNode.new(val[1], val[4]) }
| INDEX_START Expression "." "." "."
Expression INDEX_END { result = RangeNode.new(val[1], val[5], true) }
;
# The array literal.
Array:
"[" ArgList "]" { result = ArrayNode.new(val[1]) }
@@ -315,6 +333,12 @@ rule
| ArgList OUTDENT { result = val[0] }
;
# Just simple, comma-separated, required arguments (no fancy syntax).
SimpleArgs:
Expression { result = val[0] }
| SimpleArgs "," Expression { result = ([val[0]] << val[2]).flatten }
;
# Try/catch/finally exception handling blocks.
Try:
TRY Block Catch { result = TryNode.new(val[1], val[2][0], val[2][1]) }
@@ -342,6 +366,7 @@ rule
While:
WHILE Expression Block { result = WhileNode.new(val[1], val[2]) }
| WHILE Expression { result = WhileNode.new(val[1], nil) }
| Expression WHILE Expression { result = WhileNode.new(val[2], Expressions.wrap(val[0])) }
;
# Array comprehensions, including guard and current index.
@@ -384,8 +409,8 @@ rule
# An individual when.
When:
LEADING_WHEN Expression Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
| LEADING_WHEN Expression Block
LEADING_WHEN SimpleArgs Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
| LEADING_WHEN SimpleArgs Block
Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
| Comment Terminator When { result = val[2].add_comment(val[0]) }
;

View File

@@ -13,31 +13,33 @@ module CoffeeScript
"try", "catch", "finally", "throw",
"break", "continue",
"for", "in", "of", "by", "where", "while",
"delete", "instanceof", "typeof",
"switch", "when",
"super", "extends",
"arguments",
"delete", "instanceof", "typeof"]
"arguments"]
# Token matching regexes.
IDENTIFIER = /\A([a-zA-Z$_](\w|\$)*)/
NUMBER = /\A(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
STRING = /\A(""|''|"(.*?)([^\\]|\\\\)"|'(.*?)([^\\]|\\\\)')/m
HEREDOC = /\A("{6}|'{6}|"{3}\n?(\s*)(.*?)\n?(\s*)"{3}|'{3}\n?(\s*)(.*?)\n?(\s*)'{3})/m
HEREDOC = /\A("{6}|'{6}|"{3}\n?(.*?)\n?([ \t]*)"{3}|'{3}\n?(.*?)\n?([ \t]*)'{3})/m
JS = /\A(``|`(.*?)([^\\]|\\\\)`)/m
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
OPERATOR = /\A([+\*&|\/\-%=<>:!?]+)/
WHITESPACE = /\A([ \t]+)/
COMMENT = /\A(((\n?[ \t]*)?#.*$)+)/
CODE = /\A(=>)/
CODE = /\A((-|=)>)/
REGEX = /\A(\/(.*?)([^\\]|\\\\)\/[imgy]{0,4})/
MULTI_DENT = /\A((\n([ \t]*))+)(\.)?/
LAST_DENT = /\n([ \t]*)/
ASSIGNMENT = /\A(:|=)\Z/
# Token cleaning regexes.
JS_CLEANER = /(\A`|`\Z)/
MULTILINER = /\n/
COMMENT_CLEANER = /(^\s*#|\n\s*$)/
NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/
JS_CLEANER = /(\A`|`\Z)/
MULTILINER = /\n/
STRING_NEWLINES = /\n[ \t]*/
COMMENT_CLEANER = /(^[ \t]*#|\n[ \t]*$)/
NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/
HEREDOC_INDENT = /^[ \t]+/
# Tokens which a regular expression will never immediately follow, but which
# a division operator might.
@@ -45,17 +47,21 @@ module CoffeeScript
NOT_REGEX = [
:IDENTIFIER, :NUMBER, :REGEX, :STRING,
')', '++', '--', ']', '}',
:FALSE, :NULL, :THIS, :TRUE
:FALSE, :NULL, :TRUE
]
# Tokens which could legitimately be invoked or indexed.
CALLABLE = [:IDENTIFIER, :SUPER, ')', ']', '}', :STRING]
# Scan by attempting to match tokens one character at a time. Slow and steady.
def tokenize(code)
@code = code.chomp # Cleanup code by remove extra line breaks
@i = 0 # Current character position we're parsing
@line = 1 # 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]
@code = code.chomp # Cleanup code by remove extra line breaks
@i = 0 # Current character position we're parsing
@line = 1 # 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]
@spaced = nil # The last value that has a space following it.
while @i < @code.length
@chunk = @code[@i..-1]
extract_next_token
@@ -89,8 +95,15 @@ module CoffeeScript
# 'if' will result in an [:IF, "if"] token.
tag = KEYWORDS.include?(identifier) ? identifier.upcase.to_sym : :IDENTIFIER
tag = :LEADING_WHEN if tag == :WHEN && [:OUTDENT, :INDENT, "\n"].include?(last_tag)
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2] && @tokens[-2][1] == '.')
@tokens[-1][0] = :PROTOTYPE_ACCESS if tag == :IDENTIFIER && last_value == '::'
if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2] && @tokens[-2][1] == '.')
if @tokens[-2][0] == "?"
@tokens[-1][0] = :SOAK_ACCESS
@tokens.delete_at(-2)
else
@tokens[-1][0] = :PROPERTY_ACCESS
end
end
token(tag, identifier)
@i += identifier.length
end
@@ -105,7 +118,7 @@ module CoffeeScript
# Matches strings, including multi-line strings.
def string_token
return false unless string = @chunk[STRING, 1]
escaped = string.gsub(MULTILINER, " \\\n")
escaped = string.gsub(STRING_NEWLINES, " \\\n")
token(:STRING, escaped)
@line += string.count("\n")
@i += string.length
@@ -114,9 +127,10 @@ module CoffeeScript
# Matches heredocs, adjusting indentation to the correct level.
def heredoc_token
return false unless match = @chunk.match(HEREDOC)
indent = match[2] || match[5]
doc = match[3] || match[6]
doc.gsub!(/\n#{indent}/, "\\n")
doc = match[2] || match[4]
indent = doc.scan(HEREDOC_INDENT).min
doc.gsub!(/^#{indent}/, "")
doc.gsub!("\n", "\\n")
doc.gsub!('"', '\\"')
token(:STRING, "\"#{doc}\"")
@line += match[1].count("\n")
@@ -153,7 +167,7 @@ module CoffeeScript
@line += indent.scan(MULTILINER).size
@i += indent.size
next_character = @chunk[MULTI_DENT, 4]
no_newlines = next_character == '.' || (last_value.to_s.match(NO_NEWLINE) && last_value != "=>")
no_newlines = next_character == '.' || (last_value.to_s.match(NO_NEWLINE) && @tokens[-2][0] != '.' && !last_value.match(CODE))
return suppress_newlines(indent) if no_newlines
size = indent.scan(LAST_DENT).last.last.length
return newline_token(indent) if size == @indent
@@ -180,6 +194,7 @@ module CoffeeScript
# Matches and consumes non-meaningful whitespace.
def whitespace_token
return false unless whitespace = @chunk[WHITESPACE, 1]
@spaced = last_value
@i += whitespace.length
end
@@ -204,6 +219,10 @@ module CoffeeScript
tag_parameters if value && value.match(CODE)
value ||= @chunk[0,1]
tag = value.match(ASSIGNMENT) ? :ASSIGN : value
if !@spaced.equal?(last_value) && CALLABLE.include?(last_tag)
tag = :CALL_START if value == '('
tag = :INDEX_START if value == '['
end
token(tag, value)
@i += value.length
end
@@ -231,14 +250,17 @@ module CoffeeScript
# parameter identifiers in order to avoid this. Also, parameter lists can
# make use of splats.
def tag_parameters
return if last_tag != ')'
i = 0
loop do
i -= 1
tok = @tokens[i]
return if !tok
next if ['.', ','].include?(tok[0])
return if tok[0] != :IDENTIFIER
tok[0] = :PARAM
case tok[0]
when :IDENTIFIER then tok[0] = :PARAM
when ')' then tok[0] = :PARAM_END
when '(' then return tok[0] = :PARAM_START
end
end
end

View File

@@ -12,14 +12,14 @@ Readline: require('readline')
coffeePath: File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee')
# Our general-purpose error handler.
checkForErrors: coffeeProcess =>
checkForErrors: (coffeeProcess) ->
return true if coffeeProcess.wait() is 0
system.stderr.print(coffeeProcess.stderr.read())
throw new Error("CoffeeScript compile error")
# Run a simple REPL, round-tripping to the CoffeeScript compiler for every
# command.
exports.run: args =>
exports.run: (args) ->
if args.length
for path, i in args
exports.evalCS(File.read(path))
@@ -35,24 +35,24 @@ exports.run: args =>
print(e)
# Compile a given CoffeeScript file into JavaScript.
exports.compileFile: path =>
exports.compileFile: (path) ->
coffee: OS.popen([coffeePath, "--print", "--no-wrap", path])
checkForErrors(coffee)
coffee.stdout.read()
# Compile a string of CoffeeScript into JavaScript.
exports.compile: source, flags =>
exports.compile: (source, flags) ->
coffee: OS.popen([coffeePath, "--eval", "--no-wrap"].concat(flags or []))
coffee.stdin.write(source).flush().close()
checkForErrors(coffee)
coffee.stdout.read()
# Evaluating a string of CoffeeScript first compiles it externally.
exports.evalCS: source, flags =>
exports.evalCS: (source, flags) ->
eval(exports.compile(source, flags))
# Make a factory for the CoffeeScript environment.
exports.makeNarwhalFactory: path =>
exports.makeNarwhalFactory: (path) ->
code: exports.compileFile(path)
factoryText: "function(require,exports,module,system,print){" + code + "/**/\n}"
if system.engine is "rhino"

View File

@@ -20,28 +20,31 @@
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every
// command.
exports.run = function run(args) {
var __a, i, path, result;
var __a, __b, i, path, result;
if (args.length) {
__a = args;
for (i=0; i<__a.length; i++) {
for (i = 0; i < __a.length; i++) {
path = __a[i];
exports.evalCS(File.read(path));
delete args[i];
}
return true;
}
__b = [];
while (true) {
try {
system.stdout.write('coffee> ').flush();
result = exports.evalCS(Readline.readline(), ['--globals']);
if (result !== undefined) {
print(result);
__b.push((function() {
try {
system.stdout.write('coffee> ').flush();
result = exports.evalCS(Readline.readline(), ['--globals']);
if (result !== undefined) {
return print(result);
}
} catch (e) {
return print(e);
}
} catch (e) {
print(e);
}
}).call(this));
}
return null;
return __b;
};
// Compile a given CoffeeScript file into JavaScript.
exports.compileFile = function compileFile(path) {

View File

@@ -6,12 +6,12 @@ factories: {}
loader: {
# Reload the coffee-script environment from source.
reload: topId, path =>
reload: (topId, path) ->
coffeescript ||= require('coffee-script')
factories[topId]: => coffeescript.makeNarwhalFactory(path)
factories[topId]: -> coffeescript.makeNarwhalFactory(path)
# Ensure that the coffee-script environment is loaded.
load: topId, path =>
load: (topId, path) ->
factories[topId] ||= this.reload(topId, path)
}

View File

@@ -24,6 +24,19 @@ module CoffeeScript
class_eval "def statement_only?; true; end"
end
# This node needs to know if it's being compiled as a top-level statement,
# in order to compile without special expression conversion.
def self.top_sensitive
class_eval "def top_sensitive?; true; end"
end
# Provide a quick implementation of a children method.
def self.children(*attributes)
attr_reader(*attributes)
attrs = attributes.map {|a| "[@#{a}]" }.join(', ')
class_eval "def children; [#{attrs}].flatten.compact; end"
end
def write(code)
puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE']
code
@@ -36,15 +49,17 @@ module CoffeeScript
def compile(o={})
@options = o.dup
@indent = o[:indent]
top = self.is_a?(ForNode) ? @options[:top] : @options.delete(:top)
closure = statement? && !statement_only? && !top && !@options[:return]
closure ? compile_closure(@options) : compile_node(@options)
top = self.top_sensitive? ? @options[:top] : @options.delete(:top)
closure = statement? && !statement_only? && !top && !@options[:return] && !self.is_a?(CommentNode)
closure &&= !contains? {|n| n.statement_only? }
closure ? compile_closure(@options) : compile_node(@options)
end
# Statements converted into expressions share scope with their parent
# closure, to preserve JavaScript-style lexical scope.
def compile_closure(o={})
indent = o[:indent]
@indent = (o[:indent] = idt(1))
"(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
@indent = o[:indent]
ClosureNode.wrap(self).compile(o.merge(:shared_scope => o[:scope]))
end
# Quick short method for the current indentation level, plus tabbing in.
@@ -52,19 +67,30 @@ module CoffeeScript
@indent + (TAB * tabs)
end
# Does this node, or any of it's children, contain a node of a certain kind?
def contains?(&block)
children.each do |node|
return true if yield(node)
return true if node.is_a?(Node) && node.contains?(&block)
end
false
end
# Default implementations of the common node methods.
def unwrap; self; end
def children; []; end
def statement?; false; end
def statement_only?; false; end
def top_sensitive?; false; end
end
# A collection of nodes, each one representing an expression.
class Expressions < Node
statement
attr_reader :expressions
children :expressions
attr_accessor :function
TRAILING_WHITESPACE = /\s+$/
UPPERCASE = /[A-Z]/
# Wrap up a node as an Expressions, unless it already is.
def self.wrap(*nodes)
@@ -93,18 +119,17 @@ module CoffeeScript
@expressions.length == 1 ? @expressions.first : self
end
# Is this an empty block of code?
def empty?
@expressions.empty?
end
# Is the node last in this block of expressions.
def last?(node)
@last_index ||= @expressions.last.is_a?(CommentNode) ? -2 : -1
node == @expressions[@last_index]
end
# Determine if this is the expressions body within a constructor function.
# Constructors are capitalized by CoffeeScript convention.
def constructor?(o)
o[:top] && o[:last_assign] && o[:last_assign][0..0][UPPERCASE]
end
def compile(o={})
o[:scope] ? super(o) : compile_root(o)
end
@@ -118,7 +143,7 @@ module CoffeeScript
def compile_root(o={})
indent = o[:no_wrap] ? '' : TAB
@indent = indent
o.merge!(:indent => indent, :scope => Scope.new(nil, self))
o.merge!(:indent => indent, :scope => Scope.new(nil, self, nil))
code = o[:globals] ? compile_node(o) : compile_with_declarations(o)
code.gsub!(TRAILING_WHITESPACE, '')
write(o[:no_wrap] ? code : "(function(){\n#{code}\n})();")
@@ -128,8 +153,12 @@ module CoffeeScript
# at the top.
def compile_with_declarations(o={})
code = compile_node(o)
return code unless o[:scope].declarations?(self)
write("#{idt}var #{o[:scope].declared_variables.join(', ')};\n#{code}")
args = self.contains? {|n| n.is_a?(LiteralNode) && n.arguments? }
argv = args && o[:scope].check('arguments') ? '' : 'var '
code = "#{idt}#{argv}arguments = Array.prototype.slice.call(arguments, 0);\n#{code}" if args
code = "#{idt}var #{o[:scope].compiled_assignments};\n#{code}" if o[:scope].assignments?(self)
code = "#{idt}var #{o[:scope].compiled_declarations};\n#{code}" if o[:scope].declarations?(self)
write(code)
end
# Compiles a single expression within the expression list.
@@ -143,10 +172,10 @@ module CoffeeScript
# If it's a statement, the node knows how to return itself.
return node.compile(o.merge(:return => true)) if node.statement?
# If it's not part of a constructor, we can just return the value of the expression.
return "#{idt}return #{node.compile(o)};" unless constructor?(o)
return "#{idt}return #{node.compile(o)};" unless o[:scope].function && o[:scope].function.constructor?
# It's the last line of a constructor, add a safety check.
temp = o[:scope].free_variable
"#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:last_assign]} === this.constructor ? this : #{temp};"
"#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:scope].function.name} === this.constructor ? this : #{temp};"
end
end
@@ -154,17 +183,12 @@ module CoffeeScript
# Literals are static values that have a Ruby representation, eg.: a string, a number,
# true, false, nil, etc.
class LiteralNode < Node
children :value
# Values of a literal node that much be treated as a statement -- no
# sense returning or assigning them.
STATEMENTS = ['break', 'continue']
# If we get handed a literal reference to an arguments object, convert
# it to an array.
ARG_ARRAY = 'Array.prototype.slice.call(arguments, 0)'
attr_reader :value
# Wrap up a compiler-generated string as a LiteralNode.
def self.wrap(string)
self.new(Value.new(string))
@@ -179,19 +203,21 @@ module CoffeeScript
end
alias_method :statement_only?, :statement?
def arguments?
@value.to_s == 'arguments'
end
def compile_node(o)
@value = ARG_ARRAY if @value.to_s.to_sym == :arguments
indent = statement? ? idt : ''
ending = statement? ? ';' : ''
write "#{indent}#{@value}#{ending}"
"#{indent}#{@value}#{ending}"
end
end
# Return an expression, or wrap it in a closure and return it.
class ReturnNode < Node
statement_only
attr_reader :expression
children :expression
def initialize(expression)
@expression = expression
@@ -207,7 +233,7 @@ module CoffeeScript
# Pass through CoffeeScript comments into JavaScript comments at the
# same position.
class CommentNode < Node
statement_only
statement
def initialize(lines)
@lines = lines.value
@@ -223,47 +249,38 @@ module CoffeeScript
# Node for a function invocation. Takes care of converting super() calls into
# calls against the prototype's function of the same name.
class CallNode < Node
attr_reader :variable, :arguments
children :variable, :arguments
def initialize(variable, arguments=[])
@variable, @arguments = variable, arguments
@prefix = ''
end
def new_instance
@new = true
@prefix = "new "
self
end
def super?
@variable == :super
end
def prefix
@new ? "new " : ''
end
def splat?
@arguments.any? {|a| a.is_a?(ArgSplatNode) }
end
def <<(argument)
@arguments << argument
self
end
# Compile a vanilla function call.
def compile_node(o)
return write(compile_splat(o)) if splat?
return write(compile_splat(o)) if @arguments.any? {|a| a.is_a?(SplatNode) }
args = @arguments.map{|a| a.compile(o) }.join(', ')
return write(compile_super(args, o)) if super?
write("#{prefix}#{@variable.compile(o)}(#{args})")
return write(compile_super(args, o)) if @variable == 'super'
write("#{@prefix}#{@variable.compile(o)}(#{args})")
end
# Compile a call against the superclass's implementation of the current function.
def compile_super(args, o)
methname = o[:last_assign]
methname = o[:scope].function.name
arg_part = args.empty? ? '' : ", #{args}"
meth = o[:proto_assign] ? "#{o[:proto_assign]}.__superClass__.#{methname}" :
"#{methname}.__superClass__.constructor"
meth = o[:scope].function.proto ?
"#{o[:scope].function.proto}.__superClass__.#{methname}" :
"#{methname}.__superClass__.constructor"
"#{meth}.call(this#{arg_part})"
end
@@ -273,18 +290,26 @@ module CoffeeScript
obj = @variable.source || 'this'
args = @arguments.map do |arg|
code = arg.compile(o)
code = arg.is_a?(ArgSplatNode) ? code : "[#{code}]"
code = arg.is_a?(SplatNode) ? code : "[#{code}]"
arg.equal?(@arguments.first) ? code : ".concat(#{code})"
end
"#{prefix}#{meth}.apply(#{obj}, #{args.join('')})"
"#{@prefix}#{meth}.apply(#{obj}, #{args.join('')})"
end
# If the code generation wished to use the result of a function call
# in multiple places, ensure that the function is only ever called once.
def compile_reference(o)
reference = o[:scope].free_variable
call = ParentheticalNode.new(AssignNode.new(reference, self))
return call, reference
end
end
# Node to extend an object's prototype with an ancestor object.
# After goog.inherits from the Closure Library.
class ExtendsNode < Node
children :sub_object, :super_object
statement
attr_reader :sub_object, :super_object
def initialize(sub_object, super_object)
@sub_object, @super_object = sub_object, super_object
@@ -305,10 +330,14 @@ module CoffeeScript
# A value, indexed or dotted into, or vanilla.
class ValueNode < Node
attr_reader :base, :properties, :last, :source
children :base, :properties
attr_reader :last, :source
# Soak up undefined properties and call attempts.
SOAK = " == undefined ? undefined : "
def initialize(base, properties=[])
@base, @properties = base, properties
@base, @properties = base, [properties].flatten
end
def <<(other)
@@ -332,28 +361,51 @@ module CoffeeScript
properties? && @properties.last.is_a?(SliceNode)
end
def unwrap
@properties.empty? ? @base : self
end
# Values are statements if their base is a statement.
def statement?
@base.is_a?(Node) && @base.statement? && !properties?
end
def compile_node(o)
only = o.delete(:only_first)
props = only ? @properties[0...-1] : @properties
parts = [@base, props].flatten.map {|val| val.compile(o) }
soaked = false
only = o.delete(:only_first)
props = only ? @properties[0...-1] : @properties
baseline = @base.compile(o)
parts = [baseline.dup]
props.each do |prop|
if prop.is_a?(AccessorNode) && prop.soak
soaked = true
if @base.is_a?(CallNode) && prop == props.first
temp = o[:scope].free_variable
parts[-1] = "(#{temp} = #{baseline})#{SOAK}#{baseline = temp.to_s + prop.compile(o)}"
else
parts[-1] << "#{SOAK}#{baseline += prop.compile(o)}"
end
else
parts << prop.compile(o)
end
end
@last = parts.last
@source = parts.length > 1 ? parts[0...-1].join('') : nil
write(parts.join(''))
code = parts.join('').gsub(')())', '()))')
write(soaked ? "(#{code})" : code)
end
end
# A dotted accessor into a part of a value, or the :: shorthand for
# an accessor into the object's prototype.
class AccessorNode < Node
attr_reader :name
children :name
attr_reader :soak
def initialize(name, prototype=false)
@name, @prototype = name, prototype
def initialize(name, tag=nil)
@name = name
@prototype = tag == :prototype
@soak = tag == :soak
end
def compile_node(o)
@@ -364,7 +416,7 @@ module CoffeeScript
# An indexed accessor into a part of an array or object.
class IndexNode < Node
attr_reader :index
children :index
def initialize(index)
@index = index
@@ -378,7 +430,7 @@ module CoffeeScript
# A range literal. Ranges can be used to extract portions (slices) of arrays,
# or to specify a range for array comprehensions.
class RangeNode < Node
attr_reader :from, :to
children :from, :to
def initialize(from, to, exclusive=false)
@from, @to, @exclusive = from, to, exclusive
@@ -421,7 +473,7 @@ module CoffeeScript
# specifies the index of the end of the slice (just like the first parameter)
# is the index of the beginning.
class SliceNode < Node
attr_reader :range
children :range
def initialize(range)
@range = range
@@ -437,33 +489,43 @@ module CoffeeScript
# Setting the value of a local variable, or the value of an object property.
class AssignNode < Node
top_sensitive
children :variable, :value
PROTO_ASSIGN = /\A(\S+)\.prototype/
LEADING_DOT = /\A\.(prototype\.)?/
attr_reader :variable, :value, :context
def initialize(variable, value, context=nil)
@variable, @value, @context = variable, value, context
end
def compile_node(o)
return compile_pattern_match(o) if @variable.array? || @variable.object?
return compile_splice(o) if @variable.splice?
stmt = o.delete(:as_statement)
name = @variable.compile(o)
last = @variable.last.to_s.sub(LEADING_DOT, '')
proto = name[PROTO_ASSIGN, 1]
o = o.merge(:last_assign => last, :proto_assign => proto)
o[:immediate_assign] = last if @value.is_a?(CodeNode) && last.match(Lexer::IDENTIFIER)
top = o.delete(:top)
return compile_pattern_match(o) if statement?
return compile_splice(o) if value? && @variable.splice?
stmt = o.delete(:as_statement)
name = @variable.compile(o)
last = value? ? @variable.last.to_s.sub(LEADING_DOT, '') : name
proto = name[PROTO_ASSIGN, 1]
if @value.is_a?(CodeNode)
@value.name = last if last.match(Lexer::IDENTIFIER)
@value.proto = proto if proto
end
return write("#{name}: #{@value.compile(o)}") if @context == :object
o[:scope].find(name) unless @variable.properties?
o[:scope].find(name) unless value? && @variable.properties?
val = "#{name} = #{@value.compile(o)}"
return write("#{idt}#{val};") if stmt
write(o[:return] ? "#{idt}return (#{val})" : val)
val = "(#{val})" if !top || o[:return]
val = "#{idt}return #{val}" if o[:return]
write(val)
end
def value?
@variable.is_a?(ValueNode)
end
def statement?
@variable.array? || @variable.object?
value? && (@variable.array? || @variable.object?)
end
# Implementation of recursive pattern matching, when assigning array or
@@ -476,9 +538,12 @@ module CoffeeScript
@variable.base.objects.each_with_index do |obj, i|
obj, i = obj.value, obj.variable.base if @variable.object?
access_class = @variable.array? ? IndexNode : AccessorNode
assigns << AssignNode.new(
obj, ValueNode.new(Value.new(val_var), [access_class.new(Value.new(i.to_s))])
).compile(o)
if obj.is_a?(SplatNode)
val = LiteralNode.wrap(obj.compile_value(o, val_var, @variable.base.objects.index(obj)))
else
val = ValueNode.new(val_var, [access_class.new(Value.new(i.to_s))])
end
assigns << AssignNode.new(obj, val).compile(o)
end
write(assigns.join("\n"))
end
@@ -496,6 +561,10 @@ module CoffeeScript
# Simple Arithmetic and logical operations. Performs some conversion from
# CoffeeScript operations into their JavaScript equivalents.
class OpNode < Node
children :first, :second
attr_reader :operator
attr_accessor :second
CONVERSIONS = {
:== => "===",
:'!=' => "!==",
@@ -505,11 +574,10 @@ module CoffeeScript
:isnt => "!==",
:not => '!'
}
CONDITIONALS = [:'||=', :'&&=']
CHAINABLE = [:<, :>, :>=, :<=, :===, :'!===']
ASSIGNMENT = [:'||=', :'&&=', :'?=']
PREFIX_OPERATORS = [:typeof, :delete]
attr_reader :operator, :first, :second
def initialize(operator, first, second=nil, flip=false)
@first, @second, @flip = first, second, flip
@operator = CONVERSIONS[operator.to_sym] || operator
@@ -519,18 +587,39 @@ module CoffeeScript
@second.nil?
end
def chainable?
CHAINABLE.include?(operator.to_sym)
end
def compile_node(o)
return write(compile_conditional(o)) if CONDITIONALS.include?(@operator.to_sym)
return write(compile_chain(o)) if chainable? && @first.unwrap.is_a?(OpNode) && @first.unwrap.chainable?
return write(compile_assignment(o)) if ASSIGNMENT.include?(@operator.to_sym)
return write(compile_unary(o)) if unary?
return write(compile_existence(o)) if @operator == '?'
write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}")
end
def compile_conditional(o)
# Mimic Python's chained comparisons. See:
# http://docs.python.org/reference/expressions.html#notin
def compile_chain(o)
shared = @first.unwrap.second
@first.second, shared = *shared.compile_reference(o) if shared.is_a?(CallNode)
"(#{@first.compile(o)}) && (#{shared.compile(o)} #{@operator} #{@second.compile(o)})"
end
def compile_assignment(o)
first, second = @first.compile(o), @second.compile(o)
o[:scope].find(first) if @first.unwrap.is_a?(Value)
sym = @operator[0..1]
return "#{first} = #{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}" if @operator == '?='
"#{first} = #{first} #{sym} #{second}"
end
def compile_existence(o)
first, second = @first.compile(o), @second.compile(o)
"#{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}"
end
def compile_unary(o)
space = PREFIX_OPERATORS.include?(@operator.to_sym) ? ' ' : ''
parts = [@operator.to_s, space, @first.compile(o)]
@@ -540,66 +629,78 @@ module CoffeeScript
end
# A function definition. The only node that creates a new Scope.
# A CodeNode does not have any children -- they're within the new scope.
class CodeNode < Node
attr_reader :params, :body
top_sensitive
attr_reader :params, :body, :bound
attr_accessor :name, :proto
def initialize(params, body)
# Constructor functions start with an uppercase letter, by convention.
UPPERCASE = /[A-Z]/
def initialize(params, body, tag=nil)
@params = params
@body = body
@body = body
@bound = tag == :boundfunc
end
def constructor?
@name && @name[0..0][UPPERCASE]
end
def compile_node(o)
shared_scope = o.delete(:shared_scope)
o[:scope] = shared_scope || Scope.new(o[:scope], @body)
top = o.delete(:top)
o[:scope] = shared_scope || Scope.new(o[:scope], @body, self)
o[:return] = true
o[:top] = true
o[:indent] = idt(1)
o[:indent] = idt(@bound ? 2 : 1)
o.delete(:no_wrap)
o.delete(:globals)
name = o.delete(:immediate_assign)
if @params.last.is_a?(ParamSplatNode)
if @params.last.is_a?(SplatNode)
splat = @params.pop
splat.index = @params.length
@body.unshift(splat)
end
@params.each {|id| o[:scope].parameter(id.to_s) }
code = @body.compile_with_declarations(o)
name_part = name ? " #{name}" : ''
write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{idt}}")
code = @body.empty? ? "" : "\n#{@body.compile_with_declarations(o)}\n"
name_part = @name ? " #{@name}" : ''
func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt(@bound ? 1 : 0)}}"
func = "(#{func})" if top && !@bound
return write(func) unless @bound
inner = "(function#{name_part}() {\n#{idt(2)}return __func.apply(__this, arguments);\n#{idt(1)}});"
write("(function(__this) {\n#{idt(1)}var __func = #{func};\n#{idt(1)}return #{inner}\n#{idt}})(this)")
end
end
# A parameter splat in a function definition.
class ParamSplatNode < Node
# A splat, either as a parameter to a function, an argument to a call,
# or in a destructuring assignment.
class SplatNode < Node
children :name
attr_accessor :index
attr_reader :name
def initialize(name)
@name = name
end
def compile_node(o={})
write(@index ? compile_param(o) : @name.compile(o))
end
def compile_param(o)
o[:scope].find(@name)
write("#{@name} = Array.prototype.slice.call(arguments, #{@index})")
end
end
class ArgSplatNode < Node
attr_reader :value
def initialize(value)
@value = value
"#{@name} = Array.prototype.slice.call(arguments, #{@index})"
end
def compile_node(o={})
write(@value.compile(o))
def compile_value(o, name, index)
"Array.prototype.slice.call(#{name}, #{index})"
end
end
# An object literal.
class ObjectNode < Node
attr_reader :properties
children :properties
alias_method :objects, :properties
def initialize(properties = [])
@@ -626,7 +727,7 @@ module CoffeeScript
# An array literal.
class ArrayNode < Node
attr_reader :objects
children :objects
def initialize(objects=[])
@objects = objects
@@ -644,25 +745,54 @@ module CoffeeScript
end
end
# A faux-node that is never created by the grammar, but is used during
# code generation to generate a quick "array.push(value)" tree of nodes.
class PushNode
def self.wrap(array, expressions)
expr = expressions.unwrap
return expressions if expr.statement_only? || expr.contains? {|n| n.statement_only? }
Expressions.wrap(CallNode.new(
ValueNode.new(LiteralNode.new(array), [AccessorNode.new(Value.new('push'))]),
[expr]
))
end
end
# A faux-node used to wrap an expressions body in a closure.
class ClosureNode
def self.wrap(expressions, statement=false)
func = ParentheticalNode.new(CodeNode.new([], Expressions.wrap(expressions)))
call = CallNode.new(ValueNode.new(func, AccessorNode.new(Value.new('call'))), [Value.new('this')])
statement ? Expressions.wrap(call) : call
end
end
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
# it, all other loops can be manufactured.
class WhileNode < Node
top_sensitive
children :condition, :body
statement
attr_reader :condition, :body
def initialize(condition, body)
@condition, @body = condition, body
end
def compile_node(o)
returns = o.delete(:return)
top = o.delete(:top) && !returns
o[:indent] = idt(1)
o[:top] = true
cond = @condition.compile(o)
post = returns ? "\n#{idt}return null;" : ''
return write("#{idt}while (#{cond}) null;#{post}") if @body.nil?
write("#{idt}while (#{cond}) {\n#{@body.compile(o)}\n#{idt}}#{post}")
set = ''
if !top
rvar = o[:scope].free_variable
set = "#{idt}#{rvar} = [];\n"
@body = PushNode.wrap(rvar, @body)
end
post = returns ? "\n#{idt}return #{rvar};" : ''
return write("#{set}#{idt}while (#{cond}) null;#{post}") if @body.nil?
write("#{set}#{idt}while (#{cond}) {\n#{@body.compile(o)}\n#{idt}}#{post}")
end
end
@@ -671,10 +801,11 @@ module CoffeeScript
# of the comprehenion. Unlike Python array comprehensions, it's able to pass
# the current index of the loop as a second parameter.
class ForNode < Node
top_sensitive
children :body, :source, :filter
attr_reader :name, :index, :step
statement
attr_reader :body, :source, :name, :index, :filter, :step
def initialize(body, source, name, index=nil)
@body, @name, @index = body, name, index
@source = source[:source]
@@ -695,27 +826,23 @@ module CoffeeScript
rvar = scope.free_variable unless top_level
svar = scope.free_variable
ivar = range ? name : @index ? @index : scope.free_variable
var_part = ''
body = Expressions.wrap(@body)
if range
index_var = scope.free_variable
source_part = source.compile_variables(o)
for_part = "#{index_var}=0, #{source.compile(o.merge(:index => ivar, :step => @step))}, #{index_var}++"
var_part = ''
else
index_var = nil
source_part = "#{svar} = #{source.compile(o)};\n#{idt}"
for_part = @object ? "#{ivar} in #{svar}" : "#{ivar}=0; #{ivar}<#{svar}.length; #{ivar}++"
var_part = @name ? "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" : ''
for_part = @object ? "#{ivar} in #{svar}" : "#{ivar} = 0; #{ivar} < #{svar}.length; #{ivar}++"
var_part = "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" if @name
# body.unshift(AssignNode.new(@name, ValueNode.new(svar, [IndexNode.new(ivar)]))) if @name
end
body = @body
set_result = rvar ? "#{idt}#{rvar} = []; " : idt
return_result = rvar || ''
if top_level
body = Expressions.wrap(body)
else
body = Expressions.wrap(CallNode.new(
ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [body.unwrap]
))
end
body = ClosureNode.wrap(body, true) if top_level && contains? {|n| n.is_a? CodeNode }
body = PushNode.wrap(rvar, body) unless top_level
if o[:return]
return_result = "return #{return_result}" if o[:return]
o.delete(:return)
@@ -724,11 +851,13 @@ module CoffeeScript
body = Expressions.wrap(IfNode.new(@filter, body))
end
if @object
o[:scope].assign("__hasProp", "Object.prototype.hasOwnProperty", true)
body = Expressions.wrap(IfNode.new(
CallNode.new(ValueNode.new(LiteralNode.wrap(svar), [AccessorNode.new(Value.new('hasOwnProperty'))]), [LiteralNode.wrap(ivar)]),
Expressions.wrap(body),
nil,
{:statement => true}
CallNode.new(
ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]),
[LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]
),
Expressions.wrap(body), nil, {:statement => true}
))
end
@@ -741,10 +870,10 @@ module CoffeeScript
# A try/catch/finally block.
class TryNode < Node
children :try, :recovery, :finally
attr_reader :error
statement
attr_reader :try, :error, :recovery, :finally
def initialize(try, error, recovery, finally=nil)
@try, @error, @recovery, @finally = try, error, recovery, finally
end
@@ -761,10 +890,9 @@ module CoffeeScript
# Throw an exception.
class ThrowNode < Node
children :expression
statement_only
attr_reader :expression
def initialize(expression)
@expression = expression
end
@@ -776,15 +904,20 @@ module CoffeeScript
# Check an expression for existence (meaning not null or undefined).
class ExistenceNode < Node
attr_reader :expression
children :expression
def self.compile_test(o, variable)
first, second = variable, variable
first, second = *variable.compile_reference(o) if variable.is_a?(CallNode)
"(typeof #{first.compile(o)} !== \"undefined\" && #{second.compile(o)} !== null)"
end
def initialize(expression)
@expression = expression
end
def compile_node(o)
val = @expression.compile(o)
write("(typeof #{val} !== \"undefined\" && #{val} !== null)")
write(ExistenceNode.compile_test(o, @expression))
end
end
@@ -792,7 +925,7 @@ module CoffeeScript
# You can't wrap parentheses around bits that get compiled into JS statements,
# unfortunately.
class ParentheticalNode < Node
attr_reader :expressions
children :expressions
def initialize(expressions, line=nil)
@expressions = expressions.unwrap
@@ -811,13 +944,14 @@ module CoffeeScript
# Single-expression IfNodes are compiled into ternary operators if possible,
# because ternaries are first-class returnable assignable expressions.
class IfNode < Node
attr_reader :condition, :body, :else_body
children :condition, :body, :else_body
def initialize(condition, body, else_body=nil, tags={})
@condition = condition
@body = body && body.unwrap
@else_body = else_body && else_body.unwrap
@tags = tags
@multiple = true if @condition.is_a?(Array)
@condition = OpNode.new("!", ParentheticalNode.new(@condition)) if @tags[:invert]
end
@@ -839,7 +973,8 @@ module CoffeeScript
# Rewrite a chain of IfNodes with their switch condition for equality.
def rewrite_condition(expression)
@condition = OpNode.new("is", expression, @condition)
@condition = @multiple ? @condition.map {|c| OpNode.new("is", expression, c) } :
OpNode.new("is", expression, @condition)
@else_body.rewrite_condition(expression) if chain?
self
end
@@ -861,6 +996,10 @@ module CoffeeScript
@is_statement ||= !!(@comment || @tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
end
def compile_condition(o)
[@condition].flatten.map {|c| c.compile(o) }.join(' || ')
end
def compile_node(o)
write(statement? ? compile_statement(o) : compile_ternary(o))
end
@@ -876,7 +1015,8 @@ module CoffeeScript
if_dent = child ? '' : idt
com_dent = child ? idt : ''
prefix = @comment ? @comment.compile(cond_o) + "\n#{com_dent}" : ''
if_part = "#{prefix}#{if_dent}if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{idt}}"
body = Expressions.wrap(@body).compile(o)
if_part = "#{prefix}#{if_dent}if (#{compile_condition(cond_o)}) {\n#{body}\n#{idt}}"
return if_part unless @else_body
else_part = chain? ?
" else #{@else_body.compile(o.merge(:indent => idt, :chain_child => true))}" :

View File

@@ -5,16 +5,22 @@ module CoffeeScript
# line-number aware.
class ParseError < Racc::ParseError
def initialize(token_id, value, stack)
@token_id, @value, @stack = token_id, value, stack
TOKEN_MAP = {
'INDENT' => 'indent',
'OUTDENT' => 'outdent',
"\n" => 'newline'
}
def initialize(token_id, value, stack=nil, message=nil)
@token_id, @value, @stack, @message = token_id, value, stack, message
end
def message
line = @value.respond_to?(:line) ? @value.line : "END"
line_part = "line #{line}:"
id_part = @token_id != @value.inspect ? ", unexpected #{@token_id.to_s.downcase}" : ""
val_part = ['INDENT', 'OUTDENT'].include?(@token_id) ? '' : " for '#{@value.to_s}'"
"#{line_part} syntax error#{val_part}#{id_part}"
id_part = @token_id != @value.to_s ? " unexpected #{@token_id.to_s.downcase}" : ""
val_part = @message || "for #{TOKEN_MAP[@value.to_s] || "'#{@value}'"}"
"#{line_part} syntax error, #{val_part}#{id_part}"
end
alias_method :inspect, :message

2529
lib/coffee_script/parser.rb Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,8 @@ module CoffeeScript
class Rewriter
# Tokens that must be balanced.
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], [:INDENT, :OUTDENT]]
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], [:INDENT, :OUTDENT],
[:PARAM_START, :PARAM_END], [:CALL_START, :CALL_END], [:INDEX_START, :INDEX_END]]
# Tokens that signal the start of a balanced pair.
EXPRESSION_START = BALANCED_PAIRS.map {|pair| pair.first }
@@ -17,6 +18,14 @@ module CoffeeScript
# Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE = [:CATCH, :WHEN, :ELSE, :FINALLY] + EXPRESSION_TAIL
# Tokens pairs that, in immediate succession, indicate an implicit call.
IMPLICIT_FUNC = [:IDENTIFIER, :SUPER, ')', :CALL_END, ']', :INDEX_END]
IMPLICIT_END = [:IF, :UNLESS, :FOR, :WHILE, "\n", :OUTDENT]
IMPLICIT_CALL = [:IDENTIFIER, :NUMBER, :STRING, :JS, :REGEX, :NEW, :PARAM_START,
:TRY, :DELETE, :INSTANCEOF, :TYPEOF, :SWITCH, :ARGUMENTS,
:TRUE, :FALSE, :YES, :NO, :ON, :OFF, '!', '!!', :NOT,
'->', '=>', '[', '(', '{']
# The inverse mappings of token pairs we're trying to fix up.
INVERSES = BALANCED_PAIRS.inject({}) do |memo, pair|
memo[pair.first] = pair.last
@@ -26,8 +35,8 @@ module CoffeeScript
# 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 = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN]
SINGLE_LINERS = [:ELSE, "->", "=>", :TRY, :FINALLY, :THEN]
SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN, :PARAM_START]
# Rewrite the token stream in multiple passes, one logical filter at
# a time. This could certainly be changed into a single pass through the
@@ -35,8 +44,11 @@ module CoffeeScript
def rewrite(tokens)
@tokens = tokens
adjust_comments
remove_leading_newlines
remove_mid_expression_newlines
move_commas_outside_outdents
close_open_calls_and_indexes
add_implicit_parentheses
add_implicit_indentation
ensure_balance(*BALANCED_PAIRS)
rewrite_closing_parens
@@ -69,7 +81,7 @@ module CoffeeScript
@tokens.delete_at(i + 2)
@tokens.delete_at(i - 2)
next 0
elsif prev[0] == "\n" && [:INDENT, :OUTDENT].include?(after[0])
elsif prev[0] == "\n" && [:INDENT].include?(after[0])
@tokens.delete_at(i + 2)
@tokens[i - 1] = after
next 1
@@ -82,6 +94,12 @@ module CoffeeScript
end
end
# Leading newlines would introduce an ambiguity in the grammar, so we
# dispatch them here.
def remove_leading_newlines
@tokens.shift if @tokens[0][0] == "\n"
end
# Some blocks occur in the middle of expressions -- when we're expecting
# this, remove their trailing newlines.
def remove_mid_expression_newlines
@@ -104,6 +122,35 @@ module CoffeeScript
end
end
# We've tagged the opening parenthesis of a method call, and the opening
# bracket of an indexing operation. Match them with their close.
def close_open_calls_and_indexes
parens, brackets = [0], [0]
scan_tokens do |prev, token, post, i|
case token[0]
when :CALL_START then parens.push(0)
when :INDEX_START then brackets.push(0)
when '(' then parens[-1] += 1
when '[' then brackets[-1] += 1
when ')'
if parens.last == 0
parens.pop
token[0] = :CALL_END
else
parens[-1] -= 1
end
when ']'
if brackets.last == 0
brackets.pop
token[0] = :INDEX_END
else
brackets[-1] -= 1
end
end
next 1
end
end
# 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.
@@ -112,6 +159,7 @@ module CoffeeScript
scan_tokens do |prev, token, post, i|
next 1 unless SINGLE_LINERS.include?(token[0]) && post[0] != :INDENT &&
!(token[0] == :ELSE && post[0] == :IF) # Elsifs shouldn't get blocks.
starter = token[0]
line = token[1].line
@tokens.insert(i + 1, [:INDENT, Value.new(2, line)])
idx = i + 1
@@ -119,9 +167,11 @@ module CoffeeScript
loop do
idx += 1
tok = @tokens[idx]
if !tok || SINGLE_CLOSERS.include?(tok[0]) ||
(tok[0] == ')' && parens == 0)
@tokens.insert(idx, [:OUTDENT, Value.new(2, line)])
if (!tok || SINGLE_CLOSERS.include?(tok[0]) ||
(tok[0] == ')' && parens == 0)) &&
!(starter == :ELSE && tok[0] == :ELSE)
insertion = @tokens[idx - 1][0] == "," ? idx - 1 : idx
@tokens.insert(insertion, [:OUTDENT, Value.new(2, line)])
break
end
parens += 1 if tok[0] == '('
@@ -133,25 +183,52 @@ module CoffeeScript
end
end
# 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.
def add_implicit_parentheses
stack = [0]
scan_tokens do |prev, token, post, i|
stack.push(0) if token[0] == :INDENT
if token[0] == :OUTDENT
last = stack.pop
stack[-1] += last
end
if stack.last > 0 && (IMPLICIT_END.include?(token[0]) || post.nil?)
idx = token[0] == :OUTDENT ? i + 1 : i
stack.last.times { @tokens.insert(idx, [:CALL_END, Value.new(')', token[1].line)]) }
size, stack[-1] = stack[-1] + 1, 0
next size
end
next 1 unless IMPLICIT_FUNC.include?(prev[0]) && IMPLICIT_CALL.include?(token[0])
@tokens.insert(i, [:CALL_START, Value.new('(', token[1].line)])
stack[-1] += 1
next 2
end
end
# Ensure that all listed pairs of tokens are correctly balanced throughout
# the course of the token stream.
def ensure_balance(*pairs)
levels = Hash.new(0)
puts "\nbefore ensure_balance: #{@tokens.inspect}" if ENV['VERBOSE']
levels, lines = Hash.new(0), Hash.new
scan_tokens do |prev, token, post, i|
pairs.each do |pair|
open, close = *pair
levels[open] += 1 if token[0] == open
levels[open] -= 1 if token[0] == close
lines[token[0]] = token[1].line
raise ParseError.new(token[0], token[1], nil) if levels[open] < 0
end
next 1
end
unclosed = levels.detect {|k, v| v > 0 }
raise SyntaxError, "unclosed '#{unclosed[0]}'" if unclosed
sym = unclosed && unclosed[0]
raise ParseError.new(sym, Value.new(sym, lines[sym]), nil, "unclosed '#{sym}'") if unclosed
end
# We'd like to support syntax like this:
# el.click(event =>
# el.click((event) ->
# el.hide())
# In order to accomplish this, move outdents that follow closing parens
# inwards, safely. The steps to accomplish this are:

View File

@@ -5,12 +5,13 @@ module CoffeeScript
# whether a variable has been seen before or if it needs to be declared.
class Scope
attr_reader :parent, :expressions, :variables, :temp_variable
attr_reader :parent, :expressions, :function, :variables, :temp_variable
# Initialize a scope with its parent, for lookups up the chain,
# as well as the Expressions body where it should declare its variables.
def initialize(parent, expressions)
@parent, @expressions = parent, expressions
# as well as the Expressions body where it should declare its variables,
# and the function that it wraps.
def initialize(parent, expressions, function)
@parent, @expressions, @function = parent, expressions, function
@variables = {}
@temp_variable = @parent ? @parent.temp_variable.dup : '__a'
end
@@ -44,18 +45,43 @@ module CoffeeScript
def free_variable
@temp_variable.succ! while check(@temp_variable)
@variables[@temp_variable.to_sym] = :var
@temp_variable.dup
Value.new(@temp_variable.dup)
end
# Ensure that an assignment is made at the top of scope (or top-level
# scope, if requested).
def assign(name, value, top=false)
return @parent.assign(name, value, top) if top && @parent
@variables[name.to_sym] = Value.new(value)
end
def declarations?(body)
!declared_variables.empty? && body == @expressions
end
def assignments?(body)
!assigned_variables.empty? && body == @expressions
end
# Return the list of variables first declared in current scope.
def declared_variables
@variables.select {|k, v| v == :var }.map {|pair| pair[0].to_s }.sort
end
# Return the list of variables that are supposed to be assigned at the top
# of scope.
def assigned_variables
@variables.select {|k, v| v.is_a?(Value) }.sort_by {|pair| pair[0].to_s }
end
def compiled_declarations
declared_variables.join(', ')
end
def compiled_assignments
assigned_variables.map {|name, val| "#{name} = #{val}"}.join(', ')
end
def inspect
"<Scope:#{__id__} #{@variables.inspect}>"
end

View File

@@ -2,6 +2,8 @@ module CoffeeScript
# Instead of producing raw Ruby objects, the Lexer produces values of this
# class, wrapping native objects tagged with line number information.
# Values masquerade as both strings and nodes -- being used both as nodes in
# the AST, and as literally-interpolated values in the generated code.
class Value
attr_reader :value, :line
@@ -41,6 +43,22 @@ module CoffeeScript
def hash
@value.hash
end
def match(regex)
@value.match(regex)
end
def children
[]
end
def statement_only?
false
end
def contains?
false
end
end
end

View File

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

View File

@@ -1,10 +1,10 @@
area: x, y, x1, y1 =>
area: (x, y, x1, y1) ->
(x - x1) * (x - y1)
x: y: 10
x1: y1: 20
print(area(x, y, x1, y1) is 100)
print area(x, y, x1, y1) is 100
print(area(x, y,
x1, y1) is 100)
@@ -18,7 +18,15 @@ print(area(
# Arguments are turned into arrays.
curried: =>
print(area.apply(this, arguments.concat(20, 20)) is 100)
curried: ->
print area.apply(this, arguments.concat(20, 20)) is 100
curried(10, 10)
curried 10, 10
# Arguments is not a special keyword -- it can be assigned to:
func: ->
arguments: 25
arguments
print func(100) is 25

View File

@@ -1,15 +1,15 @@
nums: n * n for n in [1, 2, 3] when n % 2 isnt 0
results: n * 2 for n in nums
print(results.join(',') is '2,18')
print results.join(',') is '2,18'
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
print(names.join(' ') is "one! two! three!")
print(odds.join(' ') is "one! three!")
print names.join(' ') is "one! two! three!"
print odds.join(' ') is "one! three!"
evens: for num in [1, 2, 3, 4, 5, 6] when num % 2 is 0
@@ -17,9 +17,26 @@ evens: for num in [1, 2, 3, 4, 5, 6] when num % 2 is 0
num -= 2
num * -1
print(evens.join(', ') is '4, 6, 8')
print evens.join(', ') is '4, 6, 8'
# Make sure that the "in" operator still works.
print(2 in evens)
print 2 in evens
# When functions are being defined within the body of a comprehension, make
# sure that their safely wrapped in a closure to preserve local variables.
obj: {}
methods: ['one', 'two', 'three']
for method in methods
name: method
obj[name]: ->
"I'm " + name
print obj.one() is "I'm one"
print obj.two() is "I'm two"
print obj.three() is "I'm three"

View File

@@ -1,8 +0,0 @@
result: try
nonexistent * missing
catch error
true
result2: try nonexistent * missing catch error then true
print(result is true and result2 is true)

View File

@@ -0,0 +1,23 @@
# Assign to try/catch.
result: try
nonexistent * missing
catch error
true
result2: try nonexistent * missing catch error then true
print result is true and result2 is true
# Assign to conditional.
get_x: -> 10
if x: get_x() then 100
print x is 10
x: if get_x() then 100
print x is 100

View File

@@ -1,4 +1,4 @@
results: [1, 2, 3].map() x =>
results: [1, 2, 3].map (x) ->
x * x
print(results.join(' ') is '1 4 9')
print results.join(' ') is '1 4 9'

View File

@@ -1,36 +1,36 @@
Base: =>
Base::func: string =>
Base: ->
Base::func: (string) ->
'zero/' + string
FirstChild: =>
FirstChild: ->
FirstChild extends Base
FirstChild::func: string =>
FirstChild::func: (string) ->
super('one/') + string
SecondChild: =>
SecondChild: ->
SecondChild extends FirstChild
SecondChild::func: string =>
SecondChild::func: (string) ->
super('two/') + string
ThirdChild: =>
ThirdChild: ->
this.array: [1, 2, 3]
ThirdChild extends SecondChild
ThirdChild::func: string =>
ThirdChild::func: (string) ->
super('three/') + string
result: (new ThirdChild()).func('four')
result: (new ThirdChild()).func 'four'
print(result is 'zero/one/two/three/four')
print result is 'zero/one/two/three/four'
TopClass: arg =>
TopClass: (arg) ->
this.prop: 'top-' + arg
SuperClass: arg =>
super('super-' + arg)
SuperClass: (arg) ->
super 'super-' + arg
SubClass: =>
super('sub')
SubClass: ->
super 'sub'
SuperClass extends TopClass
SubClass extends SuperClass

View File

@@ -1,8 +1,9 @@
identity_wrap: x => => x
identity_wrap: (x) ->
-> x
result: identity_wrap(identity_wrap(true))()()
print(result)
print result
str: 'god'
@@ -13,7 +14,7 @@ result: str.
reverse().
reverse()
print(result.join('') is 'dog')
print result.join('') is 'dog'
result: str
.split('')
@@ -21,4 +22,4 @@ result: str
.reverse()
.reverse()
print(result.join('') is 'dog')
print result.join('') is 'dog'

View File

@@ -3,26 +3,26 @@ b: -2
[a, b]: [b, a]
print(a is -2)
print(b is -1)
print a is -2
print b is -1
arr: [1, 2, 3]
[a, b, c]: arr
print(a is 1)
print(b is 2)
print(c is 3)
print a is 1
print b is 2
print c is 3
obj: {x: 10, y: 20, z: 30}
{x: a, y: b, z: c}: obj
print(a is 10)
print(b is 20)
print(c is 30)
print a is 10
print b is 20
print c is 30
person: {
@@ -42,5 +42,21 @@ person: {
{name: a, family: {brother: {addresses: [one, {city: b}]}}}: person
print(a is "Bob")
print(b is "Moquasset NY, 10021")
print a is "Bob"
print b is "Moquasset NY, 10021"
test: {
person: {
address: [
"------"
"Street 101"
"Apt 101"
"City 101"
]
}
}
{person: {address: [ignore, addr...]}}: test
print addr.join(', ') is "Street 101, Apt 101, City 101"

View File

@@ -1,15 +1,15 @@
func: =>
func: ->
a: 3
b: []
while a >= 0
b.push('o')
b.push 'o'
a--
c: {
"text": b
other: null
something_else: x => x + 5
something_else: (x) -> x + 5
}
c: 'error' unless 42 > 41
@@ -26,4 +26,4 @@ func: =>
c.single: c.list[1..1][0]
print(func() == '-')
print func() is '-'

View File

@@ -2,4 +2,54 @@ print(if my_special_variable? then false else true)
my_special_variable: false
print(if my_special_variable? then true else false)
print(if my_special_variable? then true else false)
# Existential assignment.
a: 5
a: null
a ?= 10
b ?= 10
print a is 10 and b is 10
# The existential operator.
z: null
x: z ? "EX"
print z is null and x is "EX"
# Only evaluate once.
counter: 0
get_next_node: ->
throw "up" if counter
counter++
print(if get_next_node()? then true else false)
# Existence chains, soaking up undefined properties:
obj: {
prop: "hello"
}
print obj?.prop is "hello"
print obj?.prop?.non?.existent?.property is undefined
# Soaks and caches method calls as well.
arr: ["--", "----"]
print arr.pop()?.length is 4
print arr.pop()?.length is 2
print arr.pop()?.length is undefined
print arr[0]?.length is undefined
print arr.pop()?.length?.non?.existent()?.property is undefined

View File

@@ -0,0 +1,30 @@
# Ensure that we don't wrap Nodes that are "statement_only" in a closure.
items: [1, 2, 3, "bacon", 4, 5]
for item in items
break if item is "bacon"
findit: (items) ->
for item in items
return item if item is "bacon"
print findit(items) is "bacon"
# When when a closure wrapper is generated for expression conversion, make sure
# that references to "this" within the wrapper are safely converted as well.
obj: {
num: 5
func: ->
this.result: if false
10
else
"a"
"b"
this.num
}
print obj.num is obj.func()
print obj.num is obj.result

View File

@@ -7,4 +7,10 @@ result: if a
if d
true
print(result)
print result
first: if false then false else second: if false then false else true
print first
print second

View File

@@ -0,0 +1,78 @@
x: 1
y: {}
y.x: -> 3
print x is 1
print typeof(y.x) is 'function'
print y.x() is 3
print y.x.name is 'x'
# The empty function should not cause a syntax error.
->
obj: {
name: "Fred"
bound: ->
(=> print(this.name is "Fred"))()
unbound: ->
(-> print(!this.name?))()
}
obj.unbound()
obj.bound()
# The named function should be cleared out before a call occurs:
# Python decorator style wrapper that memoizes any function
memoize: (fn) ->
cache: {}
self: this
(args...) ->
key: args.toString()
return cache[key] if cache[key]
cache[key] = fn.apply(self, args)
Math: {
Add: (a, b) -> a + b
AnonymousAdd: ((a, b) -> a + b)
FastAdd: memoize (a, b) -> a + b
}
print Math.Add(5, 5) is 10
print Math.AnonymousAdd(10, 10) is 20
print Math.FastAdd(20, 20) is 40
# Parens are optional on simple function calls.
print 100 > 1 if 1 > 0
print true unless false
print true for i in [1..3]
print_func: (f) -> print(f())
print_func -> true
# Optional parens can be used in a nested fashion.
call: (func) -> func()
result: call ->
inner: call ->
Math.Add(5, 5)
print result is 10
# And even with strange things like this:
funcs: [(x) -> x, (x) -> x * x]
result: funcs[1] 5
print result is 25
result: ("hello".slice) 3
print result is 'lo'

View File

@@ -1,5 +1,5 @@
# comment
func: =>
func: ->
# comment
false
false # comment
@@ -14,4 +14,8 @@ switch 'string'
when null
something_else()
print(func())
->
code()
# comment
print func()

View File

@@ -3,7 +3,7 @@ a: """
on two lines
"""
print(a is "basic heredoc\non two lines")
print a is "basic heredoc\non two lines"
a: '''
@@ -12,12 +12,12 @@ a: '''
c
'''
print(a is "a\n \"b\nc")
print a is "a\n \"b\nc"
a: '''one-liner'''
print(a is 'one-liner')
print a is 'one-liner'
a: """
@@ -25,4 +25,22 @@ a: """
here
"""
print(a is "out\nhere")
print a is "out\nhere"
a: '''
a
b
c
'''
print a is " a\n b\nc"
a: '''
a
b c
'''
print a is "a\n\n\nb c"

View File

@@ -1,3 +1,10 @@
num: 1 + 2 + (a: 3)
print(num is 6)
print num is 6
result: if true
false
other: "result"
print result is "result" and other is "result"

View File

@@ -1,37 +1,37 @@
a: [(x => x), (x => x * x)]
a: [(x) -> x, (x) -> x * x]
print(a.length is 2)
print a.length is 2
regex: /match/i
words: "I think there is a match in here."
print(!!words.match(regex))
print !!words.match(regex)
neg: (3 -4)
print(neg is -1)
print neg is -1
func: =>
func: ->
return if true
print(func() is null)
print func() is null
str: "\\"
reg: /\\/
print(reg(str) and str is '\\')
print reg(str) and str is '\\'
i: 10
while i -= 1
print(i is 0)
print i is 0
money$: 'dollars'
print(money$ is 'dollars')
print money$ is 'dollars'

View File

@@ -1,8 +0,0 @@
x: 1
y: {}
y.x: => 3
print(x is 1)
print(typeof(y.x) is 'function')
print(y.x() is 3)
print(y.x.name is 'x')

View File

@@ -6,6 +6,6 @@ multi_liner:
single_liner:
[x, y] for y in [3..5] for x in [3..5]
print(multi_liner.length is single_liner.length)
print(5 is multi_liner[2][2][1])
print(5 is single_liner[2][2][1])
print multi_liner.length is single_liner.length
print 5 is multi_liner[2][2][1]
print 5 is single_liner[2][2][1]

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