mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-13 08:47:55 -05:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63c9b5c2f0 | ||
|
|
80fbe02fda | ||
|
|
e514a39dd2 | ||
|
|
4a32c58221 | ||
|
|
4609ad78c2 | ||
|
|
2d90a751f7 | ||
|
|
8647b54a61 | ||
|
|
8e1f3c0eca | ||
|
|
c4d0903e6a | ||
|
|
e72ef1a61a | ||
|
|
d7d9cb8d28 | ||
|
|
f6c8e81ea6 | ||
|
|
52539ae7d2 | ||
|
|
95b362499f | ||
|
|
0bc4da2b51 | ||
|
|
9679fc0b52 | ||
|
|
9cb0564972 | ||
|
|
c6c0c7d059 | ||
|
|
62e946b8ce | ||
|
|
6c782b7723 | ||
|
|
9eff443032 | ||
|
|
8957feedb4 | ||
|
|
1cd7fa8ebe | ||
|
|
701cdb4c13 | ||
|
|
8dc5da9cc9 | ||
|
|
001cc29deb | ||
|
|
e77e520607 | ||
|
|
ed8a54995d | ||
|
|
2d206e7b60 | ||
|
|
bb9fdd3015 | ||
|
|
1e7d638435 | ||
|
|
0ceca0778c | ||
|
|
abd9ab5c71 | ||
|
|
ea349a1a59 | ||
|
|
f0d5db7e66 | ||
|
|
914ba1c244 | ||
|
|
844ea33274 | ||
|
|
87e04e9952 | ||
|
|
197914bcf7 | ||
|
|
8dfbd1a2a8 | ||
|
|
c19647ad33 | ||
|
|
27f7ef09af | ||
|
|
9a61bbf005 | ||
|
|
c8d505e85d | ||
|
|
477c510345 | ||
|
|
c3029faca7 | ||
|
|
186797a745 | ||
|
|
d54fa2f2a1 | ||
|
|
5e1e949bf6 | ||
|
|
6c980d8adc | ||
|
|
2f63439bff | ||
|
|
c7cb308b6d | ||
|
|
9cc7d6af27 | ||
|
|
cfa357cbc3 | ||
|
|
9d8668f37f | ||
|
|
d1ddeacbe3 | ||
|
|
d9d09a9a72 | ||
|
|
a1528f3f19 | ||
|
|
7d2a955e0a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,4 +3,5 @@ test.coffee
|
||||
parser.output
|
||||
lib/coffee_script/parser.rb
|
||||
test/fixtures/underscore
|
||||
examples/beautiful_code/parse.coffee
|
||||
*.gem
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 Jeremy Ashkenas
|
||||
Copyright (c) 2010 Jeremy Ashkenas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'coffee-script'
|
||||
s.version = '0.2.2' # Keep version in sync with coffee-script.rb
|
||||
s.date = '2010-1-10'
|
||||
s.version = '0.2.6' # Keep version in sync with coffee-script.rb
|
||||
s.date = '2010-1-17'
|
||||
|
||||
s.homepage = "http://jashkenas.github.com/coffee-script/"
|
||||
s.summary = "The CoffeeScript Compiler"
|
||||
|
||||
5
documentation/coffee/comparisons.coffee
Normal file
5
documentation/coffee/comparisons.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
cholesterol: 127
|
||||
|
||||
healthy: 200 > cholesterol > 60
|
||||
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
solipsism: true if mind? and not world?
|
||||
solipsism: true if mind? and not world?
|
||||
|
||||
speed ?= 140
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# The first ten global properties.
|
||||
|
||||
globals: (name for name ino window)[0...10]
|
||||
globals: (name for name of window)[0...10]
|
||||
@@ -1,2 +1,2 @@
|
||||
square: x => x * x
|
||||
cube: x => square(x) * x
|
||||
cube: x => square(x) * x
|
||||
|
||||
5
documentation/coffee/heredocs.coffee
Normal file
5
documentation/coffee/heredocs.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
html: '''
|
||||
<strong>
|
||||
cup of coffeescript
|
||||
</strong>
|
||||
'''
|
||||
6
documentation/coffee/long_arrow.coffee
Normal file
6
documentation/coffee/long_arrow.coffee
Normal file
@@ -0,0 +1,6 @@
|
||||
Account: customer, cart =>
|
||||
this.customer: customer
|
||||
this.cart: cart
|
||||
|
||||
$('.shopping_cart').bind('click') event ==>
|
||||
this.customer.purchase(this.cart)
|
||||
5
documentation/coffee/multiple_return_values.coffee
Normal file
5
documentation/coffee/multiple_return_values.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
weather_report: location =>
|
||||
# Make an Ajax request to fetch the weather...
|
||||
[location, 72, "Mostly Sunny"]
|
||||
|
||||
[city, temp, forecast]: weather_report("Berkeley, CA")
|
||||
@@ -1,3 +1,4 @@
|
||||
years_old: {max: 10, ida: 9, tim: 11}
|
||||
|
||||
ages: child + " is " + age for child, age ino years_old
|
||||
ages: for child, age of years_old
|
||||
child + " is " + age
|
||||
13
documentation/coffee/object_extraction.coffee
Normal file
13
documentation/coffee/object_extraction.coffee
Normal file
@@ -0,0 +1,13 @@
|
||||
futurists: {
|
||||
sculptor: "Umberto Boccioni"
|
||||
painter: "Vladimir Burliuk"
|
||||
poet: {
|
||||
name: "F.T. Marinetti"
|
||||
address: [
|
||||
"Via Roma 42R"
|
||||
"Bellagio, Italy 22021"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
{poet: {name: poet, address: [street, city]}}: futurists
|
||||
4
documentation/coffee/parallel_assignment.coffee
Normal file
4
documentation/coffee/parallel_assignment.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
bait: 1000
|
||||
and_switch: 0
|
||||
|
||||
[bait, and_switch]: [and_switch, bait]
|
||||
@@ -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()
|
||||
@@ -1,5 +1,8 @@
|
||||
while demand > supply
|
||||
sell()
|
||||
restock()
|
||||
if this.studying_economics
|
||||
while supply > demand then buy()
|
||||
while supply < demand then sell()
|
||||
|
||||
while supply > demand then buy()
|
||||
num: 6
|
||||
lyrics: while num -= 1
|
||||
num + " little monkeys, jumping on the bed.
|
||||
One fell out and bumped his head."
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
<p>
|
||||
<b>Latest Version:</b>
|
||||
<a href="http://gemcutter.org/gems/coffee-script">0.2.2</a>
|
||||
<a href="http://gemcutter.org/gems/coffee-script">0.2.6</a>
|
||||
</p>
|
||||
|
||||
<h2>Table of Contents</h2>
|
||||
@@ -65,7 +65,7 @@
|
||||
<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="#existence">The Existential Operator</a><br />
|
||||
<a href="#aliases">Aliases</a><br />
|
||||
<a href="#splats">Splats...</a><br />
|
||||
<a href="#arguments">Arguments are Arrays</a><br />
|
||||
@@ -75,10 +75,13 @@
|
||||
<a href="#expressions">Everything is an Expression</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="#long_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="#strings">Multiline Strings</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 />
|
||||
@@ -95,7 +98,7 @@
|
||||
<a href="documentation/underscore.html">Underscore.coffee</a>, a port
|
||||
of the <a href="http://documentcloud.github.com/underscore/">Underscore.js</a>
|
||||
library of helper functions. Underscore.coffee can pass the entire Underscore.js
|
||||
test suite. The CoffeeScript version is faster than the original for a number
|
||||
test suite. The CoffeeScript version is faster than the original for a number
|
||||
of methods (in general, due to the speed of CoffeeScript's array comprehensions), and
|
||||
after being minified and gzipped, is only 241 bytes larger than the original
|
||||
JavaScript version.
|
||||
@@ -256,7 +259,8 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
<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.
|
||||
functions in CoffeeScript are named by default, for the benefit of debug messages.
|
||||
If you'd like to create an anonymous function, just wrap it in parentheses.
|
||||
</p>
|
||||
<%= code_for('functions', 'cube(5)') %>
|
||||
|
||||
@@ -327,14 +331,18 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
</p>
|
||||
|
||||
<p id="existence">
|
||||
<b class="header">The Existence Operator</b>
|
||||
<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. The existence operator <tt>?</tt> returns true unless
|
||||
the empty string, and false. The 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>
|
||||
<%= code_for('existence') %>
|
||||
<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 id="aliases">
|
||||
<b class="header">Aliases</b>
|
||||
@@ -386,9 +394,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
|
||||
@@ -415,8 +426,8 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
<%= code_for('range_comprehensions', 'countdown') %>
|
||||
<p>
|
||||
Comprehensions can also be used to iterate over the keys and values in
|
||||
an object. Use <tt>ino</tt> to signal comprehension over an object instead
|
||||
of an array.
|
||||
an object. Use <tt>of</tt> to signal comprehension over the properties of
|
||||
an object instead of the values in an array.
|
||||
</p>
|
||||
<%= code_for('object_comprehensions', 'ages.join(", ")') %>
|
||||
|
||||
@@ -468,6 +479,12 @@ 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: <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="inheritance">
|
||||
<b class="header">Inheritance, and Calling Super from a Subclass</b>
|
||||
@@ -481,12 +498,12 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
be completely usable if it weren't for a couple of small exceptions:
|
||||
it's awkward to call <b>super</b> (the prototype object's
|
||||
implementation of the current function), and it's awkward to correctly
|
||||
set the prototype chain.
|
||||
set the prototype chain.
|
||||
</p>
|
||||
<p>
|
||||
CoffeeScript provides <tt>extends</tt>
|
||||
to help with prototype setup, <tt>::</tt> for quick access to an
|
||||
object's prototype, and converts <tt>super()</tt> into a call against
|
||||
object's prototype, and converts <tt>super()</tt> into a call against
|
||||
the immediate ancestor's method of the same name.
|
||||
</p>
|
||||
<%= code_for('super', true) %>
|
||||
@@ -499,11 +516,50 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
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,
|
||||
CoffeeScript implements ECMAScript Harmony's proposed
|
||||
<a href="http://wiki.ecmascript.org/doku.php?id=harmony:destructuring">destructuring assignment</a>
|
||||
syntax. When you assign an array or object literal to a value, CoffeeScript
|
||||
breaks up and matches both sides against each other, assigning the values
|
||||
on the right to the variables on the left. In the simplest case, it can be
|
||||
used for parallel assignment:
|
||||
</p>
|
||||
<%= code_for('parallel_assignment', 'bait') %>
|
||||
<p>
|
||||
But it's also helpful for dealing with functions that return multiple
|
||||
values.
|
||||
</p>
|
||||
<%= code_for('multiple_return_values', 'forecast') %>
|
||||
<p>
|
||||
Pattern matching can be used with any depth of array and object nesting,
|
||||
to help pull out deeply nested properties.
|
||||
</p>
|
||||
<%= code_for('object_extraction', 'poet + " — " + street') %>
|
||||
|
||||
<p id="long_arrow">
|
||||
<b class="header">Function binding</b>
|
||||
The long 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 long arrow are able to access
|
||||
properties of the <tt>this</tt> where they're defined.
|
||||
</p>
|
||||
<%= code_for('long_arrow') %>
|
||||
|
||||
<p id="embedded">
|
||||
<b class="header">Embedded JavaScript</b>
|
||||
If you ever need to interpolate literal JavaScript snippets, you can
|
||||
use backticks to pass JavaScript straight through.
|
||||
Hopefully, you'll never need to use it, but if you ever need to intersperse
|
||||
snippets of JavaScript within your CoffeeScript, you can
|
||||
use backticks to pass it straight through.
|
||||
</p>
|
||||
<%= code_for('embedded', 'hi()') %>
|
||||
|
||||
@@ -517,6 +573,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">
|
||||
@@ -526,11 +587,27 @@ 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 — 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</b>
|
||||
<b class="header">Multiline Strings and Heredocs</b>
|
||||
Multiline strings are allowed in CoffeeScript.
|
||||
</p>
|
||||
<%= code_for('strings', 'moby_dick') %>
|
||||
<p>
|
||||
Heredocs can be used to hold formatted or indentation-sensitive text
|
||||
(or, if you just don't feel like escaping quotes and apostrophes). The
|
||||
indentation level that begins the heredoc is maintained throughout, so
|
||||
you can keep it all aligned with the body of your code.
|
||||
</p>
|
||||
<%= code_for('heredocs') %>
|
||||
|
||||
<h2 id="resources">Resources</h2>
|
||||
|
||||
@@ -539,7 +616,7 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
<a href="http://github.com/jashkenas/coffee-script/">Source Code</a><br />
|
||||
After checking out the source, make sure to run <tt>rake build:parser</tt>
|
||||
to generate an up-to-date version of the Racc parser.
|
||||
Use <tt>bin/coffee</tt> to test your changes,
|
||||
Use <tt>bin/coffee</tt> to test your changes,
|
||||
<tt>rake test</tt> to run the test suite,
|
||||
and <tt>rake gem:install</tt> to
|
||||
create and install a custom version of the gem.
|
||||
@@ -554,6 +631,12 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
that includes CoffeeScript helpers,
|
||||
bundling and minification.
|
||||
</li>
|
||||
<li>
|
||||
<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="contributing">Contributing</h2>
|
||||
@@ -589,6 +672,36 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
|
||||
<h2 id="change_log">Change Log</h2>
|
||||
|
||||
<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 —
|
||||
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
|
||||
extracting values from nested arrays and objects. Added indentation-sensitive
|
||||
heredocs for nicely formatted strings or chunks of code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.3</b>
|
||||
Axed the unsatisfactory <tt>ino</tt> keyword, replacing it with <tt>of</tt> for
|
||||
object comprehensions. They now look like: <tt>for prop, value of object</tt>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.2</b>
|
||||
When performing a comprehension over an object, use <tt>ino</tt>, instead
|
||||
@@ -619,7 +732,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.
|
||||
|
||||
@@ -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");
|
||||
})();
|
||||
@@ -2,19 +2,19 @@
|
||||
var __a, __b, __c, __d, __e, __f, __g, food, lunch, roid, roid2;
|
||||
// Eat lunch.
|
||||
lunch = (function() {
|
||||
__c = []; __a = ['toast', 'cheese', 'wine'];
|
||||
for (__b=0; __b<__a.length; __b++) {
|
||||
food = __a[__b];
|
||||
__c.push(eat(food));
|
||||
__a = []; __b = ['toast', 'cheese', 'wine'];
|
||||
for (__c = 0; __c < __b.length; __c++) {
|
||||
food = __b[__c];
|
||||
__a.push(eat(food));
|
||||
}
|
||||
return __c;
|
||||
return __a;
|
||||
})();
|
||||
// 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)) {
|
||||
|
||||
5
documentation/js/comparisons.js
Normal file
5
documentation/js/comparisons.js
Normal file
@@ -0,0 +1,5 @@
|
||||
(function(){
|
||||
var cholesterol, healthy;
|
||||
cholesterol = 127;
|
||||
healthy = (200 > cholesterol) && (cholesterol > 60);
|
||||
})();
|
||||
@@ -1,5 +1,5 @@
|
||||
(function(){
|
||||
var date, mood;
|
||||
var date, expensive, mood;
|
||||
if (singing) {
|
||||
mood = greatly_improved;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
})();
|
||||
@@ -1,4 +1,4 @@
|
||||
(function(){
|
||||
var one, six, three, two;
|
||||
six = (one = 1) + (two = 2) + (three = 3);
|
||||
six = ((one = 1)) + ((two = 2)) + ((three = 3));
|
||||
})();
|
||||
@@ -1,13 +1,14 @@
|
||||
(function(){
|
||||
var __a, __b, globals, name;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
// The first ten global properties.
|
||||
globals = ((function() {
|
||||
__b = []; __a = window;
|
||||
for (name in __a) {
|
||||
if (__a.hasOwnProperty(name)) {
|
||||
__b.push(name);
|
||||
__a = []; __b = window;
|
||||
for (name in __b) {
|
||||
if (__hasProp.call(__b, name)) {
|
||||
__a.push(name);
|
||||
}
|
||||
}
|
||||
return __b;
|
||||
return __a;
|
||||
})()).slice(0, 10);
|
||||
})();
|
||||
4
documentation/js/heredocs.js
Normal file
4
documentation/js/heredocs.js
Normal file
@@ -0,0 +1,4 @@
|
||||
(function(){
|
||||
var html;
|
||||
html = "<strong>\n cup of coffeescript\n</strong>";
|
||||
})();
|
||||
17
documentation/js/long_arrow.js
Normal file
17
documentation/js/long_arrow.js
Normal 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;
|
||||
};
|
||||
})();
|
||||
11
documentation/js/multiple_return_values.js
Normal file
11
documentation/js/multiple_return_values.js
Normal file
@@ -0,0 +1,11 @@
|
||||
(function(){
|
||||
var __a, city, forecast, temp, weather_report;
|
||||
weather_report = function weather_report(location) {
|
||||
// Make an Ajax request to fetch the weather...
|
||||
return [location, 72, "Mostly Sunny"];
|
||||
};
|
||||
__a = weather_report("Berkeley, CA");
|
||||
city = __a[0];
|
||||
temp = __a[1];
|
||||
forecast = __a[2];
|
||||
})();
|
||||
@@ -1,18 +1,19 @@
|
||||
(function(){
|
||||
var __a, __b, age, ages, child, years_old;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
years_old = {
|
||||
max: 10,
|
||||
ida: 9,
|
||||
tim: 11
|
||||
};
|
||||
ages = (function() {
|
||||
__b = []; __a = years_old;
|
||||
for (child in __a) {
|
||||
age = __a[child];
|
||||
if (__a.hasOwnProperty(child)) {
|
||||
__b.push(child + " is " + age);
|
||||
__a = []; __b = years_old;
|
||||
for (child in __b) {
|
||||
age = __b[child];
|
||||
if (__hasProp.call(__b, child)) {
|
||||
__a.push(child + " is " + age);
|
||||
}
|
||||
}
|
||||
return __b;
|
||||
return __a;
|
||||
})();
|
||||
})();
|
||||
17
documentation/js/object_extraction.js
Normal file
17
documentation/js/object_extraction.js
Normal file
@@ -0,0 +1,17 @@
|
||||
(function(){
|
||||
var __a, __b, __c, city, futurists, poet, street;
|
||||
futurists = {
|
||||
sculptor: "Umberto Boccioni",
|
||||
painter: "Vladimir Burliuk",
|
||||
poet: {
|
||||
name: "F.T. Marinetti",
|
||||
address: ["Via Roma 42R", "Bellagio, Italy 22021"]
|
||||
}
|
||||
};
|
||||
__a = futurists;
|
||||
__b = __a.poet;
|
||||
poet = __b.name;
|
||||
__c = __b.address;
|
||||
street = __c[0];
|
||||
city = __c[1];
|
||||
})();
|
||||
@@ -33,11 +33,11 @@
|
||||
}
|
||||
// Array comprehensions:
|
||||
cubed_list = (function() {
|
||||
__c = []; __a = list;
|
||||
for (__b=0; __b<__a.length; __b++) {
|
||||
num = __a[__b];
|
||||
__c.push(math.cube(num));
|
||||
__a = []; __b = list;
|
||||
for (__c = 0; __c < __b.length; __c++) {
|
||||
num = __b[__c];
|
||||
__a.push(math.cube(num));
|
||||
}
|
||||
return __c;
|
||||
return __a;
|
||||
})();
|
||||
})();
|
||||
8
documentation/js/parallel_assignment.js
Normal file
8
documentation/js/parallel_assignment.js
Normal file
@@ -0,0 +1,8 @@
|
||||
(function(){
|
||||
var __a, and_switch, bait;
|
||||
bait = 1000;
|
||||
and_switch = 0;
|
||||
__a = [and_switch, bait];
|
||||
bait = __a[0];
|
||||
and_switch = __a[1];
|
||||
})();
|
||||
@@ -1,21 +1,21 @@
|
||||
(function(){
|
||||
var __a, __b, __c, __d, __e, countdown, egg_delivery, num;
|
||||
countdown = (function() {
|
||||
__b = []; __d = 10; __e = 1;
|
||||
__a = []; __d = 10; __e = 1;
|
||||
for (__c=0, num=__d; (__d <= __e ? num <= __e : num >= __e); (__d <= __e ? num += 1 : num -= 1), __c++) {
|
||||
__b.push(num);
|
||||
__a.push(num);
|
||||
}
|
||||
return __b;
|
||||
return __a;
|
||||
})();
|
||||
egg_delivery = function egg_delivery() {
|
||||
var __f, __g, __h, __i, __j, dozen_eggs, i;
|
||||
__g = []; __i = 0; __j = eggs.length;
|
||||
__f = []; __i = 0; __j = eggs.length;
|
||||
for (__h=0, i=__i; (__i <= __j ? i < __j : i > __j); (__i <= __j ? i += 12 : i -= 12), __h++) {
|
||||
__g.push((function() {
|
||||
__f.push((function() {
|
||||
dozen_eggs = eggs.slice(i, i + 12);
|
||||
return deliver(new egg_carton(dozen));
|
||||
})());
|
||||
}
|
||||
return __g;
|
||||
return __f;
|
||||
};
|
||||
})();
|
||||
@@ -1,6 +1,6 @@
|
||||
(function(){
|
||||
var contenders, gold, medalists, silver, the_field;
|
||||
gold = silver = the_field = "unknown";
|
||||
gold = (silver = (the_field = "unknown"));
|
||||
medalists = function medalists(first, second) {
|
||||
var rest;
|
||||
rest = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
@@ -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.");
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
(function(){
|
||||
while (demand > supply) {
|
||||
sell();
|
||||
restock();
|
||||
}
|
||||
while (supply > demand) {
|
||||
buy();
|
||||
var __a, lyrics, num;
|
||||
if (this.studying_economics) {
|
||||
while (supply > demand) {
|
||||
buy();
|
||||
}
|
||||
while (supply < demand) {
|
||||
sell();
|
||||
}
|
||||
}
|
||||
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;
|
||||
})();
|
||||
})();
|
||||
@@ -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>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<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">=></span>
|
||||
<span class="line-numbers"> 25 </span> <span class="Variable">this</span>.<span class="FunctionName">_wrapped</span><span class="Keyword">:</span> obj
|
||||
<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>
|
||||
<span class="line-numbers"> 28 </span>
|
||||
@@ -54,7 +54,7 @@
|
||||
<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>
|
||||
<span class="line-numbers"> 38 </span> <span class="Keyword">if</span> <span class="Keyword">typeof</span>(exports) <span class="Keyword">!</span><span class="Keyword">=</span> <span class="String"><span class="String">'</span>undefined<span class="String">'</span></span> <span class="Keyword">then</span> exports.<span class="FunctionName">_</span><span class="Keyword">:</span> _
|
||||
<span class="line-numbers"> 38 </span> <span class="Keyword">if</span> <span class="Keyword">typeof</span>(exports) <span class="Keyword">!</span><span class="Keyword">=</span> <span class="String"><span class="String">'</span>undefined<span class="String">'</span></span> <span class="Keyword">then</span> <span class="FunctionName">exports._</span><span class="Keyword">:</span> _
|
||||
<span class="line-numbers"> 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>
|
||||
@@ -66,7 +66,7 @@
|
||||
<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.5<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>
|
||||
@@ -79,7 +79,7 @@
|
||||
<span class="line-numbers"> 60 </span> <span class="Keyword">return</span> obj.forEach(iterator, context) <span class="Keyword">if</span> obj.forEach
|
||||
<span class="line-numbers"> 61 </span> <span class="Keyword">if</span> _.isArray(obj) <span class="Keyword">or</span> _.isArguments(obj)
|
||||
<span class="line-numbers"> 62 </span> <span class="Keyword">return</span> iterator.call(context, obj[i], i, obj) <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...obj.length]
|
||||
<span class="line-numbers"> 63 </span> iterator.call(context, val, key, obj) <span class="Keyword">for</span> key, val <span class="Keyword">ino</span> obj
|
||||
<span class="line-numbers"> 63 </span> iterator.call(context, val, key, obj) <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
|
||||
<span class="line-numbers"> 64 </span> <span class="Keyword">catch</span> e
|
||||
<span class="line-numbers"> 65 </span> <span class="Keyword">throw</span> e <span class="Keyword">if</span> e <span class="Keyword">isnt</span> breaker
|
||||
<span class="line-numbers"> 66 </span> obj
|
||||
@@ -167,7 +167,7 @@
|
||||
<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">=></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">ino</span> 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
|
||||
<span class="line-numbers"> 153 </span> <span class="BuiltInConstant">false</span>
|
||||
<span class="line-numbers"> 154 </span>
|
||||
@@ -399,7 +399,7 @@
|
||||
<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">=></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">ino</span> 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"> 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>
|
||||
@@ -414,7 +414,7 @@
|
||||
<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">=></span>
|
||||
<span class="line-numbers"> 398 </span> <span class="Keyword">for</span> key, val <span class="Keyword">ino</span> source
|
||||
<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"> 401 </span>
|
||||
@@ -522,7 +522,7 @@
|
||||
<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">=></span>
|
||||
<span class="line-numbers"> 506 </span> root.<span class="FunctionName">_</span><span class="Keyword">:</span> previousUnderscore
|
||||
<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>
|
||||
@@ -561,15 +561,15 @@
|
||||
<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"> 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"> 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>
|
||||
@@ -605,7 +605,7 @@
|
||||
<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">=></span>
|
||||
<span class="line-numbers"> 589 </span> <span class="Variable">this</span>.<span class="FunctionName">_chain</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</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>
|
||||
|
||||
16
examples/beautiful_code/binary_search.coffee
Normal file
16
examples/beautiful_code/binary_search.coffee
Normal 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))
|
||||
13
examples/beautiful_code/quicksort_runtime.coffee
Normal file
13
examples/beautiful_code/quicksort_runtime.coffee
Normal 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)
|
||||
34
examples/beautiful_code/regular_expression_matcher.coffee
Normal file
34
examples/beautiful_code/regular_expression_matcher.coffee
Normal 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"))
|
||||
@@ -15,8 +15,8 @@ dc.model.Document: dc.Model.extend({
|
||||
# document by binding to Metadata, instead of on-the-fly.
|
||||
metadata: =>
|
||||
docId: this.id
|
||||
_.select(Metadata.models(), (meta =>
|
||||
_.any(meta.get('instances'), instance =>
|
||||
_.select(Metadata.models(), (meta =>
|
||||
_.any(meta.get('instances'), instance =>
|
||||
instance.document_id is docId)))
|
||||
|
||||
bookmark: pageNumber =>
|
||||
@@ -60,7 +60,7 @@ dc.model.DocumentSet: dc.model.RESTfulSet.extend({
|
||||
# change their selected state.
|
||||
_onModelEvent: e, model =>
|
||||
this.base(e, model)
|
||||
fire: e == dc.Model.CHANGED and model.hasChanged('selected')
|
||||
fire: e is dc.Model.CHANGED and model.hasChanged('selected')
|
||||
if fire then _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this))
|
||||
|
||||
})
|
||||
|
||||
205
examples/potion.coffee
Normal file
205
examples/potion.coffee
Normal file
@@ -0,0 +1,205 @@
|
||||
# Examples from _why's Potion, the Readme and "Potion: A Short Pamphlet".
|
||||
|
||||
# 5 times: "Odelay!" print.
|
||||
|
||||
print("Odelay!") for i in [1..5]
|
||||
|
||||
|
||||
# add = (x, y): x + y.
|
||||
# add(2, 4) string print
|
||||
|
||||
add: x, y => x + y
|
||||
print(add(2, 4))
|
||||
|
||||
|
||||
# loop: 'quaff' print.
|
||||
|
||||
while true
|
||||
print('quaff')
|
||||
|
||||
|
||||
# ('cheese', 'bread', 'mayo') at (1) print
|
||||
|
||||
print(['cheese', 'bread', 'mayo'][1])
|
||||
|
||||
|
||||
# (language='Potion', pointless=true) at (key='language') print
|
||||
|
||||
print({language: 'Potion', pointless: true}['language'])
|
||||
|
||||
|
||||
# minus = (x, y): x - y.
|
||||
# minus (y=10, x=6)
|
||||
|
||||
minus: x, y => x - y
|
||||
minus(6, 10)
|
||||
|
||||
|
||||
# foods = ('cheese', 'bread', 'mayo')
|
||||
# foods (2)
|
||||
|
||||
foods: ['cheese', 'bread', 'mayo']
|
||||
foods[2]
|
||||
|
||||
|
||||
# (dog='canine', cat='feline', fox='vulpine') each (key, val):
|
||||
# (key, ' is a ', val) join print.
|
||||
|
||||
for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
|
||||
print(key + ' is a ' + val)
|
||||
|
||||
|
||||
# Person = class: /name, /age, /sex.
|
||||
# Person print = ():
|
||||
# ('My name is ', /name, '.') join print.
|
||||
|
||||
Person: =>
|
||||
Person::print: =>
|
||||
print('My name is ' + this.name + '.')
|
||||
|
||||
|
||||
# p = Person ()
|
||||
# p /name string print
|
||||
|
||||
p: new Person()
|
||||
print(p.name)
|
||||
|
||||
|
||||
# Policeman = Person class (rank): /rank = rank.
|
||||
# Policeman print = ():
|
||||
# ('My name is ', /name, ' and I'm a ', /rank, '.') join print.
|
||||
#
|
||||
# Policeman ('Constable') print
|
||||
|
||||
Policeman: rank => this.rank: rank
|
||||
Policeman extends Person
|
||||
Policeman::print: =>
|
||||
print('My name is ' + this.name + " and I'm a " + this.rank + '.')
|
||||
|
||||
print(new Policeman('Constable'))
|
||||
|
||||
|
||||
# app = [window (width=200, height=400)
|
||||
# [para 'Welcome.', button 'OK']]
|
||||
# app first name
|
||||
|
||||
app = {
|
||||
window: {width: 200, height: 200}
|
||||
para: 'Welcome.'
|
||||
button: 'OK'
|
||||
}
|
||||
app.window
|
||||
|
||||
|
||||
# x = 1
|
||||
# y = 2
|
||||
#
|
||||
# x = 1, y = 2
|
||||
|
||||
x: 1
|
||||
y: 2
|
||||
|
||||
x: 1; y: 2
|
||||
|
||||
|
||||
# table = (language='Potion'
|
||||
# pointless=true)
|
||||
|
||||
table: {
|
||||
language: 'Potion'
|
||||
pointless: yes
|
||||
}
|
||||
|
||||
|
||||
# # this foul business...
|
||||
# String length = (): 10.
|
||||
|
||||
# this foul business...
|
||||
String::length: => 10
|
||||
|
||||
|
||||
# block = :
|
||||
# 'potion' print.
|
||||
|
||||
block: =>
|
||||
print('potion')
|
||||
|
||||
|
||||
# if (age > 100): 'ancient'.
|
||||
|
||||
if age > 100 then 'ancient'
|
||||
|
||||
|
||||
# author =
|
||||
# if (title == 'Jonathan Strange & Mr. Norrell'):
|
||||
# 'Susanna Clarke'.
|
||||
# elsif (title == 'The Star Diaries'):
|
||||
# 'Stanislaw Lem'.
|
||||
# elsif (title == 'The Slynx'):
|
||||
# 'Tatyana Tolstaya'.
|
||||
# else:
|
||||
# '... probably Philip K. Dick'.
|
||||
|
||||
switch author
|
||||
when 'Jonathan Strange & Mr. Norrell'
|
||||
'Susanna Clarke'
|
||||
when 'The Star Diaries'
|
||||
'Stanislaw Lem'
|
||||
when 'The Slynx'
|
||||
'Tatyana Tolstaya'
|
||||
else
|
||||
'... probably Philip K. Dick'
|
||||
|
||||
|
||||
# count = 8
|
||||
# while (count > 0):
|
||||
# 'quaff' print
|
||||
# count--.
|
||||
|
||||
count: 8
|
||||
while count > 0
|
||||
print('quaff')
|
||||
count--
|
||||
|
||||
|
||||
# 1 to 5 (a):
|
||||
# a string print.
|
||||
|
||||
print(a) for a in [1..5]
|
||||
|
||||
|
||||
# if (3 ?gender):
|
||||
# "Huh? Numbers are sexed? That's amazing." print.
|
||||
|
||||
if (3).gender?
|
||||
print("Huh? Numbers are sexed? That's amazing.")
|
||||
|
||||
|
||||
# HomePage get = (url):
|
||||
# session = url query ? at ('session').
|
||||
|
||||
HomePage::get: url =>
|
||||
session: url.query.session if url.query?
|
||||
|
||||
|
||||
# BTree = class: /left, /right.
|
||||
# b = BTree ()
|
||||
# b /left = BTree ()
|
||||
# b /right = BTree ()
|
||||
|
||||
BTree: =>
|
||||
b: new BTree()
|
||||
b.left: new BTree()
|
||||
b.right: new BTree()
|
||||
|
||||
|
||||
# BTree = class: /left, /right.
|
||||
# b = BTree ()
|
||||
#
|
||||
# if (b ? /left):
|
||||
# 'left path found!' print.
|
||||
|
||||
BTree: =>
|
||||
b: new BTree()
|
||||
|
||||
print('left path found!') if b.left?
|
||||
@@ -60,7 +60,7 @@
|
||||
return obj.forEach(iterator, context) if obj.forEach
|
||||
if _.isArray(obj) or _.isArguments(obj)
|
||||
return iterator.call(context, obj[i], i, obj) for i in [0...obj.length]
|
||||
iterator.call(context, val, key, obj) for key, val ino obj
|
||||
iterator.call(context, val, key, obj) for key, val of obj
|
||||
catch e
|
||||
throw e if e isnt breaker
|
||||
obj
|
||||
@@ -148,7 +148,7 @@
|
||||
# based on '==='.
|
||||
_.include: obj, target =>
|
||||
return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
|
||||
for key, val ino obj
|
||||
for key, val of obj
|
||||
return true if val is target
|
||||
false
|
||||
|
||||
@@ -380,7 +380,7 @@
|
||||
# Retrieve the names of an object's properties.
|
||||
_.keys: obj =>
|
||||
return _.range(0, obj.length) if _.isArray(obj)
|
||||
key for key, val ino obj
|
||||
key for key, val of obj
|
||||
|
||||
|
||||
# Retrieve the values of an object's properties.
|
||||
@@ -395,7 +395,7 @@
|
||||
|
||||
# Extend a given object with all of the properties in a source object.
|
||||
_.extend: destination, source =>
|
||||
for key, val ino source
|
||||
for key, val of source
|
||||
destination[key]: val
|
||||
destination
|
||||
|
||||
|
||||
495
index.html
495
index.html
@@ -37,7 +37,7 @@
|
||||
|
||||
<p>
|
||||
<b>Latest Version:</b>
|
||||
<a href="http://gemcutter.org/gems/coffee-script">0.2.2</a>
|
||||
<a href="http://gemcutter.org/gems/coffee-script">0.2.6</a>
|
||||
</p>
|
||||
|
||||
<h2>Table of Contents</h2>
|
||||
@@ -51,7 +51,7 @@
|
||||
<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="#existence">The Existential Operator</a><br />
|
||||
<a href="#aliases">Aliases</a><br />
|
||||
<a href="#splats">Splats...</a><br />
|
||||
<a href="#arguments">Arguments are Arrays</a><br />
|
||||
@@ -61,10 +61,13 @@
|
||||
<a href="#expressions">Everything is an Expression</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="#long_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="#strings">Multiline Strings</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 />
|
||||
@@ -137,12 +140,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>() {
|
||||
__c <span class="Keyword">=</span> []; __a <span class="Keyword">=</span> list;
|
||||
<span class="Keyword">for</span> (__b<span class="Keyword">=</span><span class="Number">0</span>; __b<span class="Keyword"><</span>__a.<span class="LibraryConstant">length</span>; __b<span class="Keyword">++</span>) {
|
||||
num <span class="Keyword">=</span> __a[__b];
|
||||
__c.<span class="LibraryFunction">push</span>(math.cube(num));
|
||||
__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"><</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> __c;
|
||||
<span class="Keyword">return</span> __a;
|
||||
})();
|
||||
</pre><button onclick='javascript: var __a, __b, __c, cubed_list, list, math, num, number, opposite_day, race, square;
|
||||
// Assignment:
|
||||
@@ -178,12 +181,12 @@ if ((typeof elvis !== "undefined" && elvis !== null)) {
|
||||
}
|
||||
// Array comprehensions:
|
||||
cubed_list = (function() {
|
||||
__c = []; __a = list;
|
||||
for (__b=0; __b<__a.length; __b++) {
|
||||
num = __a[__b];
|
||||
__c.push(math.cube(num));
|
||||
__a = []; __b = list;
|
||||
for (__c = 0; __c < __b.length; __c++) {
|
||||
num = __b[__c];
|
||||
__a.push(math.cube(num));
|
||||
}
|
||||
return __c;
|
||||
return __a;
|
||||
})();
|
||||
;alert(cubed_list);'>run: cubed_list</button><br class='clear' /></div>
|
||||
|
||||
@@ -192,7 +195,7 @@ cubed_list = (function() {
|
||||
<a href="documentation/underscore.html">Underscore.coffee</a>, a port
|
||||
of the <a href="http://documentcloud.github.com/underscore/">Underscore.js</a>
|
||||
library of helper functions. Underscore.coffee can pass the entire Underscore.js
|
||||
test suite. The CoffeeScript version is faster than the original for a number
|
||||
test suite. The CoffeeScript version is faster than the original for a number
|
||||
of methods (in general, due to the speed of CoffeeScript's array comprehensions), and
|
||||
after being minified and gzipped, is only 241 bytes larger than the original
|
||||
JavaScript version.
|
||||
@@ -353,7 +356,8 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
<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.
|
||||
functions in CoffeeScript are named by default, for the benefit of debug messages.
|
||||
If you'd like to create an anonymous function, just wrap it in parentheses.
|
||||
</p>
|
||||
<div class='code'><pre class="idle"><span class="FunctionName">square</span><span class="Keyword">:</span> <span class="FunctionArgument">x</span> <span class="Storage">=></span> x <span class="Keyword">*</span> x
|
||||
<span class="FunctionName">cube</span><span class="Keyword">:</span> <span class="FunctionArgument">x</span> <span class="Storage">=></span> square(x) <span class="Keyword">*</span> x
|
||||
@@ -498,7 +502,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,19 +521,36 @@ expensive <span class="Keyword">=</span> expensive <span class="Keyword">||</spa
|
||||
</p>
|
||||
|
||||
<p id="existence">
|
||||
<b class="header">The Existence Operator</b>
|
||||
<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. The existence operator <tt>?</tt> returns true unless
|
||||
the empty string, and false. The 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>
|
||||
</pre><pre class="idle"><span class="Storage">var</span> solipsism;
|
||||
|
||||
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">"</span>undefined<span class="String">"</span></span> <span class="Keyword">&</span><span class="Keyword">&</span> mind <span class="Keyword">!</span><span class="Keyword">==</span> <span class="BuiltInConstant">null</span>) <span class="Keyword">&</span><span class="Keyword">&</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">"</span>undefined<span class="String">"</span></span> <span class="Keyword">&</span><span class="Keyword">&</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>
|
||||
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">"</span>undefined<span class="String">"</span></span> <span class="Keyword">&</span><span class="Keyword">&</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 id="aliases">
|
||||
<b class="header">Aliases</b>
|
||||
@@ -612,7 +633,7 @@ alert(<span class="String"><span class="String">"</span>Gold: <span class="
|
||||
alert(<span class="String"><span class="String">"</span>Silver: <span class="String">"</span></span> <span class="Keyword">+</span> silver)
|
||||
alert(<span class="String"><span class="String">"</span>The Field: <span class="String">"</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">"</span>unknown<span class="String">"</span></span>;
|
||||
gold <span class="Keyword">=</span> (silver <span class="Keyword">=</span> (the_field <span class="Keyword">=</span> <span class="String"><span class="String">"</span>unknown<span class="String">"</span></span>));
|
||||
medalists <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">medalists</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>);
|
||||
@@ -626,7 +647,7 @@ medalists.<span class="LibraryFunction">apply</span>(<span class="Variable">this
|
||||
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">"</span>Silver: <span class="String">"</span></span> <span class="Keyword">+</span> silver);
|
||||
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">"</span>The Field: <span class="String">"</span></span> <span class="Keyword">+</span> the_field);
|
||||
</pre><button onclick='javascript: var contenders, gold, medalists, silver, the_field;
|
||||
gold = silver = the_field = "unknown";
|
||||
gold = (silver = (the_field = "unknown"));
|
||||
medalists = function medalists(first, second) {
|
||||
var rest;
|
||||
rest = Array.prototype.slice.call(arguments, 2);
|
||||
@@ -654,33 +675,70 @@ alert("The Field: " + the_field);
|
||||
backwards(<span class="String"><span class="String">"</span>stairway<span class="String">"</span></span>, <span class="String"><span class="String">"</span>to<span class="String">"</span></span>, <span class="String"><span class="String">"</span>heaven<span class="String">"</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">"</span>stairway<span class="String">"</span></span>, <span class="String"><span class="String">"</span>to<span class="String">"</span></span>, <span class="String"><span class="String">"</span>heaven<span class="String">"</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">></span> supply
|
||||
sell()
|
||||
restock()
|
||||
<div class='code'><pre class="idle"><span class="Keyword">if</span> <span class="Variable">this</span>.studying_economics
|
||||
<span class="Keyword">while</span> supply <span class="Keyword">></span> demand <span class="Keyword">then</span> buy()
|
||||
<span class="Keyword">while</span> supply <span class="Keyword"><</span> demand <span class="Keyword">then</span> sell()
|
||||
|
||||
<span class="Keyword">while</span> supply <span class="Keyword">></span> demand <span class="Keyword">then</span> buy()
|
||||
</pre><pre class="idle"><span class="Keyword">while</span> (demand <span class="Keyword">></span> supply) {
|
||||
sell();
|
||||
restock();
|
||||
<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">"</span> little monkeys, jumping on the bed.</span>
|
||||
<span class="String"> One fell out and bumped his head.<span class="String">"</span></span>
|
||||
</pre><pre class="idle"><span class="Storage">var</span> __a, lyrics, num;
|
||||
<span class="Keyword">if</span> (<span class="Variable">this</span>.studying_economics) {
|
||||
<span class="Keyword">while</span> (supply <span class="Keyword">></span> demand) {
|
||||
buy();
|
||||
}
|
||||
<span class="Keyword">while</span> (supply <span class="Keyword"><</span> demand) {
|
||||
sell();
|
||||
}
|
||||
}
|
||||
<span class="Keyword">while</span> (supply <span class="Keyword">></span> demand) {
|
||||
buy();
|
||||
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">"</span> little monkeys, jumping on the bed. \</span>
|
||||
<span class="String">One fell out and bumped his head.<span class="String">"</span></span>);
|
||||
}
|
||||
<span class="Keyword">return</span> __a;
|
||||
})();
|
||||
</pre><button onclick='javascript: var __a, lyrics, num;
|
||||
if (this.studying_economics) {
|
||||
while (supply > demand) {
|
||||
buy();
|
||||
}
|
||||
while (supply < demand) {
|
||||
sell();
|
||||
}
|
||||
}
|
||||
</pre><br class='clear' /></div>
|
||||
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;
|
||||
})();
|
||||
;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
|
||||
@@ -707,19 +765,19 @@ backwards("stairway", "to", "heaven");
|
||||
</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>() {
|
||||
__c <span class="Keyword">=</span> []; __a <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> (__b<span class="Keyword">=</span><span class="Number">0</span>; __b<span class="Keyword"><</span>__a.<span class="LibraryConstant">length</span>; __b<span class="Keyword">++</span>) {
|
||||
food <span class="Keyword">=</span> __a[__b];
|
||||
__c.<span class="LibraryFunction">push</span>(eat(food));
|
||||
__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"><</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> __c;
|
||||
<span class="Keyword">return</span> __a;
|
||||
})();
|
||||
<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"><</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"><</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"><</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"><</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)) {
|
||||
@@ -743,82 +801,85 @@ __d <span class="Keyword">=</span> asteroids;
|
||||
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>() {
|
||||
__b <span class="Keyword">=</span> []; __d <span class="Keyword">=</span> <span class="Number">10</span>; __e <span class="Keyword">=</span> <span class="Number">1</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>;
|
||||
<span class="Keyword">for</span> (__c<span class="Keyword">=</span><span class="Number">0</span>, num<span class="Keyword">=</span>__d; (__d <span class="Keyword"><=</span> __e ? num <span class="Keyword"><=</span> __e : num <span class="Keyword">>=</span> __e); (__d <span class="Keyword"><=</span> __e ? num <span class="Keyword">+</span><span class="Keyword">=</span> <span class="Number">1</span> : num <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">1</span>), __c<span class="Keyword">++</span>) {
|
||||
__b.<span class="LibraryFunction">push</span>(num);
|
||||
__a.<span class="LibraryFunction">push</span>(num);
|
||||
}
|
||||
<span class="Keyword">return</span> __b;
|
||||
<span class="Keyword">return</span> __a;
|
||||
})();
|
||||
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;
|
||||
__g <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>;
|
||||
__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>;
|
||||
<span class="Keyword">for</span> (__h<span class="Keyword">=</span><span class="Number">0</span>, i<span class="Keyword">=</span>__i; (__i <span class="Keyword"><=</span> __j ? i <span class="Keyword"><</span> __j : i <span class="Keyword">></span> __j); (__i <span class="Keyword"><=</span> __j ? i <span class="Keyword">+</span><span class="Keyword">=</span> <span class="Number">12</span> : i <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">12</span>), __h<span class="Keyword">++</span>) {
|
||||
__g.<span class="LibraryFunction">push</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="Keyword">return</span> __g;
|
||||
<span class="Keyword">return</span> __f;
|
||||
};
|
||||
</pre><button onclick='javascript: var __a, __b, __c, __d, __e, countdown, egg_delivery, num;
|
||||
countdown = (function() {
|
||||
__b = []; __d = 10; __e = 1;
|
||||
__a = []; __d = 10; __e = 1;
|
||||
for (__c=0, num=__d; (__d <= __e ? num <= __e : num >= __e); (__d <= __e ? num += 1 : num -= 1), __c++) {
|
||||
__b.push(num);
|
||||
__a.push(num);
|
||||
}
|
||||
return __b;
|
||||
return __a;
|
||||
})();
|
||||
egg_delivery = function egg_delivery() {
|
||||
var __f, __g, __h, __i, __j, dozen_eggs, i;
|
||||
__g = []; __i = 0; __j = eggs.length;
|
||||
__f = []; __i = 0; __j = eggs.length;
|
||||
for (__h=0, i=__i; (__i <= __j ? i < __j : i > __j); (__i <= __j ? i += 12 : i -= 12), __h++) {
|
||||
__g.push((function() {
|
||||
__f.push((function() {
|
||||
dozen_eggs = eggs.slice(i, i + 12);
|
||||
return deliver(new egg_carton(dozen));
|
||||
})());
|
||||
}
|
||||
return __g;
|
||||
return __f;
|
||||
};
|
||||
;alert(countdown);'>run: countdown</button><br class='clear' /></div>
|
||||
<p>
|
||||
Comprehensions can also be used to iterate over the keys and values in
|
||||
an object. Use <tt>ino</tt> to signal comprehension over an object instead
|
||||
of an array.
|
||||
an object. Use <tt>of</tt> to signal comprehension over the properties of
|
||||
an object instead of the values in an array.
|
||||
</p>
|
||||
<div class='code'><pre class="idle"><span class="FunctionName">years_old</span><span class="Keyword">:</span> {<span class="FunctionName">max</span><span class="Keyword">:</span> <span class="Number">10</span>, <span class="FunctionName">ida</span><span class="Keyword">:</span> <span class="Number">9</span>, <span class="FunctionName">tim</span><span class="Keyword">:</span> <span class="Number">11</span>}
|
||||
|
||||
<span class="FunctionName">ages</span><span class="Keyword">:</span> child <span class="Keyword">+</span> <span class="String"><span class="String">"</span> is <span class="String">"</span></span> <span class="Keyword">+</span> age <span class="Keyword">for</span> child, age <span class="Keyword">ino</span> years_old
|
||||
<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">"</span> is <span class="String">"</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>,
|
||||
tim: <span class="Number">11</span>
|
||||
};
|
||||
ages <span class="Keyword">=</span> (<span class="Storage">function</span>() {
|
||||
__b <span class="Keyword">=</span> []; __a <span class="Keyword">=</span> years_old;
|
||||
<span class="Keyword">for</span> (child <span class="Keyword">in</span> __a) {
|
||||
age <span class="Keyword">=</span> __a[child];
|
||||
<span class="Keyword">if</span> (__a.hasOwnProperty(child)) {
|
||||
__b.<span class="LibraryFunction">push</span>(child <span class="Keyword">+</span> <span class="String"><span class="String">"</span> is <span class="String">"</span></span> <span class="Keyword">+</span> age);
|
||||
__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> (__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">"</span> is <span class="String">"</span></span> <span class="Keyword">+</span> age);
|
||||
}
|
||||
}
|
||||
<span class="Keyword">return</span> __b;
|
||||
<span class="Keyword">return</span> __a;
|
||||
})();
|
||||
</pre><button onclick='javascript: var __a, __b, age, ages, child, years_old;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
years_old = {
|
||||
max: 10,
|
||||
ida: 9,
|
||||
tim: 11
|
||||
};
|
||||
ages = (function() {
|
||||
__b = []; __a = years_old;
|
||||
for (child in __a) {
|
||||
age = __a[child];
|
||||
if (__a.hasOwnProperty(child)) {
|
||||
__b.push(child + " is " + age);
|
||||
__a = []; __b = years_old;
|
||||
for (child in __b) {
|
||||
age = __b[child];
|
||||
if (__hasProp.call(__b, child)) {
|
||||
__a.push(child + " is " + age);
|
||||
}
|
||||
}
|
||||
return __b;
|
||||
return __a;
|
||||
})();
|
||||
;alert(ages.join(", "));'>run: ages.join(", ")</button><br class='clear' /></div>
|
||||
|
||||
@@ -915,9 +976,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
|
||||
@@ -927,28 +988,30 @@ six = (one = 1) + (two = 2) + (three = 3);
|
||||
</p>
|
||||
<div class='code'><pre class="idle"><span class="Comment"><span class="Comment">#</span> The first ten global properties.</span>
|
||||
|
||||
<span class="FunctionName">globals</span><span class="Keyword">:</span> (name <span class="Keyword">for</span> name <span class="Keyword">ino</span> window)[<span class="Number">0</span>...<span class="Number">10</span>]
|
||||
<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>() {
|
||||
__b <span class="Keyword">=</span> []; __a <span class="Keyword">=</span> <span class="LibraryClassType">window</span>;
|
||||
<span class="Keyword">for</span> (name <span class="Keyword">in</span> __a) {
|
||||
<span class="Keyword">if</span> (__a.hasOwnProperty(name)) {
|
||||
__b.<span class="LibraryFunction">push</span>(name);
|
||||
__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> (__hasProp.<span class="LibraryFunction">call</span>(__b, name)) {
|
||||
__a.<span class="LibraryFunction">push</span>(name);
|
||||
}
|
||||
}
|
||||
<span class="Keyword">return</span> __b;
|
||||
<span class="Keyword">return</span> __a;
|
||||
})()).<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() {
|
||||
__b = []; __a = window;
|
||||
for (name in __a) {
|
||||
if (__a.hasOwnProperty(name)) {
|
||||
__b.push(name);
|
||||
__a = []; __b = window;
|
||||
for (name in __b) {
|
||||
if (__hasProp.call(__b, name)) {
|
||||
__a.push(name);
|
||||
}
|
||||
}
|
||||
return __b;
|
||||
return __a;
|
||||
})()).slice(0, 10);
|
||||
;alert(globals);'>run: globals</button><br class='clear' /></div>
|
||||
<p>
|
||||
@@ -976,6 +1039,12 @@ globals = ((function() {
|
||||
}
|
||||
})());
|
||||
;'>run</button><br class='clear' /></div>
|
||||
<p>
|
||||
There are a handful of statements in JavaScript that can't be meaningfully
|
||||
converted into expressions: <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="inheritance">
|
||||
<b class="header">Inheritance, and Calling Super from a Subclass</b>
|
||||
@@ -989,25 +1058,25 @@ globals = ((function() {
|
||||
be completely usable if it weren't for a couple of small exceptions:
|
||||
it's awkward to call <b>super</b> (the prototype object's
|
||||
implementation of the current function), and it's awkward to correctly
|
||||
set the prototype chain.
|
||||
set the prototype chain.
|
||||
</p>
|
||||
<p>
|
||||
CoffeeScript provides <tt>extends</tt>
|
||||
to help with prototype setup, <tt>::</tt> for quick access to an
|
||||
object's prototype, and converts <tt>super()</tt> into a call against
|
||||
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">=></span>
|
||||
<span class="FunctionName">Animal::move</span><span class="Keyword">:</span> <span class="FunctionArgument">meters</span> <span class="Storage">=></span>
|
||||
alert(<span class="Variable">this</span>.name <span class="Keyword">+</span> <span class="String"><span class="String">"</span> moved <span class="String">"</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">"</span>m.<span class="String">"</span></span>)
|
||||
|
||||
<span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=></span> <span class="Variable">this</span>.<span class="FunctionName">name</span><span class="Keyword">:</span> name
|
||||
<span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=></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">=></span>
|
||||
alert(<span class="String"><span class="String">"</span>Slithering...<span class="String">"</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">=></span> <span class="Variable">this</span>.<span class="FunctionName">name</span><span class="Keyword">:</span> name
|
||||
<span class="FunctionName">Horse</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=></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">=></span>
|
||||
alert(<span class="String"><span class="String">"</span>Galloping...<span class="String">"</span></span>)
|
||||
@@ -1023,8 +1092,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">"</span> moved <span class="String">"</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">"</span>m.<span class="String">"</span></span>);
|
||||
};
|
||||
@@ -1061,8 +1129,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.");
|
||||
};
|
||||
@@ -1117,12 +1184,154 @@ tom.move();
|
||||
<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,
|
||||
CoffeeScript implements ECMAScript Harmony's proposed
|
||||
<a href="http://wiki.ecmascript.org/doku.php?id=harmony:destructuring">destructuring assignment</a>
|
||||
syntax. When you assign an array or object literal to a value, CoffeeScript
|
||||
breaks up and matches both sides against each other, assigning the values
|
||||
on the right to the variables on the left. In the simplest case, it can be
|
||||
used for parallel assignment:
|
||||
</p>
|
||||
<div class='code'><pre class="idle"><span class="FunctionName">bait</span><span class="Keyword">:</span> <span class="Number">1000</span>
|
||||
<span class="FunctionName">and_switch</span><span class="Keyword">:</span> <span class="Number">0</span>
|
||||
|
||||
[bait, and_switch]<span class="Keyword">:</span> [and_switch, bait]
|
||||
</pre><pre class="idle"><span class="Storage">var</span> __a, and_switch, bait;
|
||||
bait <span class="Keyword">=</span> <span class="Number">1000</span>;
|
||||
and_switch <span class="Keyword">=</span> <span class="Number">0</span>;
|
||||
__a <span class="Keyword">=</span> [and_switch, bait];
|
||||
bait <span class="Keyword">=</span> __a[<span class="Number">0</span>];
|
||||
and_switch <span class="Keyword">=</span> __a[<span class="Number">1</span>];
|
||||
</pre><button onclick='javascript: var __a, and_switch, bait;
|
||||
bait = 1000;
|
||||
and_switch = 0;
|
||||
__a = [and_switch, bait];
|
||||
bait = __a[0];
|
||||
and_switch = __a[1];
|
||||
;alert(bait);'>run: bait</button><br class='clear' /></div>
|
||||
<p>
|
||||
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">=></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">"</span>Mostly Sunny<span class="String">"</span></span>]
|
||||
|
||||
[city, temp, forecast]<span class="Keyword">:</span> weather_report(<span class="String"><span class="String">"</span>Berkeley, CA<span class="String">"</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>
|
||||
<span class="Keyword">return</span> [location, <span class="Number">72</span>, <span class="String"><span class="String">"</span>Mostly Sunny<span class="String">"</span></span>];
|
||||
};
|
||||
__a <span class="Keyword">=</span> weather_report(<span class="String"><span class="String">"</span>Berkeley, CA<span class="String">"</span></span>);
|
||||
city <span class="Keyword">=</span> __a[<span class="Number">0</span>];
|
||||
temp <span class="Keyword">=</span> __a[<span class="Number">1</span>];
|
||||
forecast <span class="Keyword">=</span> __a[<span class="Number">2</span>];
|
||||
</pre><button onclick='javascript: var __a, city, forecast, temp, weather_report;
|
||||
weather_report = function weather_report(location) {
|
||||
// Make an Ajax request to fetch the weather...
|
||||
return [location, 72, "Mostly Sunny"];
|
||||
};
|
||||
__a = weather_report("Berkeley, CA");
|
||||
city = __a[0];
|
||||
temp = __a[1];
|
||||
forecast = __a[2];
|
||||
;alert(forecast);'>run: forecast</button><br class='clear' /></div>
|
||||
<p>
|
||||
Pattern matching can be used with any depth of array and object nesting,
|
||||
to help pull out deeply nested properties.
|
||||
</p>
|
||||
<div class='code'><pre class="idle"><span class="FunctionName">futurists</span><span class="Keyword">:</span> {
|
||||
<span class="FunctionName">sculptor</span><span class="Keyword">:</span> <span class="String"><span class="String">"</span>Umberto Boccioni<span class="String">"</span></span>
|
||||
<span class="FunctionName">painter</span><span class="Keyword">:</span> <span class="String"><span class="String">"</span>Vladimir Burliuk<span class="String">"</span></span>
|
||||
<span class="FunctionName">poet</span><span class="Keyword">:</span> {
|
||||
<span class="FunctionName">name</span><span class="Keyword">:</span> <span class="String"><span class="String">"</span>F.T. Marinetti<span class="String">"</span></span>
|
||||
<span class="FunctionName">address</span><span class="Keyword">:</span> [
|
||||
<span class="String"><span class="String">"</span>Via Roma 42R<span class="String">"</span></span>
|
||||
<span class="String"><span class="String">"</span>Bellagio, Italy 22021<span class="String">"</span></span>
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
{<span class="FunctionName">poet</span><span class="Keyword">:</span> {<span class="FunctionName">name</span><span class="Keyword">:</span> poet, <span class="FunctionName">address</span><span class="Keyword">:</span> [street, city]}}<span class="Keyword">:</span> futurists
|
||||
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, city, futurists, poet, street;
|
||||
futurists <span class="Keyword">=</span> {
|
||||
sculptor: <span class="String"><span class="String">"</span>Umberto Boccioni<span class="String">"</span></span>,
|
||||
painter: <span class="String"><span class="String">"</span>Vladimir Burliuk<span class="String">"</span></span>,
|
||||
poet: {
|
||||
name: <span class="String"><span class="String">"</span>F.T. Marinetti<span class="String">"</span></span>,
|
||||
address: [<span class="String"><span class="String">"</span>Via Roma 42R<span class="String">"</span></span>, <span class="String"><span class="String">"</span>Bellagio, Italy 22021<span class="String">"</span></span>]
|
||||
}
|
||||
};
|
||||
__a <span class="Keyword">=</span> futurists;
|
||||
__b <span class="Keyword">=</span> __a.poet;
|
||||
poet <span class="Keyword">=</span> __b.<span class="LibraryConstant">name</span>;
|
||||
__c <span class="Keyword">=</span> __b.address;
|
||||
street <span class="Keyword">=</span> __c[<span class="Number">0</span>];
|
||||
city <span class="Keyword">=</span> __c[<span class="Number">1</span>];
|
||||
</pre><button onclick='javascript: var __a, __b, __c, city, futurists, poet, street;
|
||||
futurists = {
|
||||
sculptor: "Umberto Boccioni",
|
||||
painter: "Vladimir Burliuk",
|
||||
poet: {
|
||||
name: "F.T. Marinetti",
|
||||
address: ["Via Roma 42R", "Bellagio, Italy 22021"]
|
||||
}
|
||||
};
|
||||
__a = futurists;
|
||||
__b = __a.poet;
|
||||
poet = __b.name;
|
||||
__c = __b.address;
|
||||
street = __c[0];
|
||||
city = __c[1];
|
||||
;alert(poet + " — " + street);'>run: poet + " — " + street</button><br class='clear' /></div>
|
||||
|
||||
<p id="long_arrow">
|
||||
<b class="header">Function binding</b>
|
||||
The long 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 long 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">customer, cart</span> <span class="Storage">=></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="FunctionName">event</span> <span class="Keyword">=</span><span class="Storage">=></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>
|
||||
If you ever need to interpolate literal JavaScript snippets, you can
|
||||
use backticks to pass JavaScript straight through.
|
||||
Hopefully, you'll never need to use it, but if you ever need to intersperse
|
||||
snippets of JavaScript within your CoffeeScript, you can
|
||||
use backticks to pass it straight through.
|
||||
</p>
|
||||
<div class='code'><pre class="idle"><span class="FunctionName">hi</span><span class="Keyword">:</span> <span class="String"><span class="String">`</span>function() {</span>
|
||||
<span class="String"> return [document.title, "Hello JavaScript"].join(": ");</span>
|
||||
@@ -1149,25 +1358,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">"</span>Tuesday<span class="String">"</span></span> <span class="Keyword">then</span> eat_breakfast()
|
||||
<span class="Keyword">when</span> <span class="String"><span class="String">"</span>Wednesday<span class="String">"</span></span> <span class="Keyword">then</span> go_to_the_park()
|
||||
<span class="Keyword">when</span> <span class="String"><span class="String">"</span>Saturday<span class="String">"</span></span>
|
||||
<span class="Keyword">when</span> <span class="String"><span class="String">"</span>Mon<span class="String">"</span></span> <span class="Keyword">then</span> go_to_work()
|
||||
<span class="Keyword">when</span> <span class="String"><span class="String">"</span>Tue<span class="String">"</span></span> <span class="Keyword">then</span> go_to_the_park()
|
||||
<span class="Keyword">when</span> <span class="String"><span class="String">"</span>Thu<span class="String">"</span></span> <span class="Keyword">then</span> go_ice_fishing()
|
||||
<span class="Keyword">when</span> <span class="String"><span class="String">"</span>Fri<span class="String">"</span></span>, <span class="String"><span class="String">"</span>Sat<span class="String">"</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">"</span>Sunday<span class="String">"</span></span> <span class="Keyword">then</span> go_to_church()
|
||||
<span class="Keyword">when</span> <span class="String"><span class="String">"</span>Sun<span class="String">"</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">"</span>Tuesday<span class="String">"</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">"</span>Wednesday<span class="String">"</span></span>) {
|
||||
</pre><pre class="idle"><span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">"</span>Mon<span class="String">"</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">"</span>Tue<span class="String">"</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">"</span>Saturday<span class="String">"</span></span>) {
|
||||
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">"</span>Thu<span class="String">"</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">"</span>Fri<span class="String">"</span></span> <span class="Keyword">||</span> day <span class="Keyword">===</span> <span class="String"><span class="String">"</span>Sat<span class="String">"</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">"</span>Sunday<span class="String">"</span></span>) {
|
||||
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">"</span>Sun<span class="String">"</span></span>) {
|
||||
go_to_church();
|
||||
} <span class="Keyword">else</span> {
|
||||
go_to_work();
|
||||
@@ -1196,8 +1413,28 @@ 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 — 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">></span> cholesterol <span class="Keyword">></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">></span> cholesterol) <span class="Keyword">&</span><span class="Keyword">&</span> (cholesterol <span class="Keyword">></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</b>
|
||||
<b class="header">Multiline Strings and Heredocs</b>
|
||||
Multiline strings are allowed in CoffeeScript.
|
||||
</p>
|
||||
<div class='code'><pre class="idle"><span class="FunctionName">moby_dick</span><span class="Keyword">:</span> <span class="String"><span class="String">"</span>Call me Ishmael. Some years ago --</span>
|
||||
@@ -1223,6 +1460,20 @@ to interest me on shore, I thought I would sail \
|
||||
about a little and see the watery part of the \
|
||||
world...";
|
||||
;alert(moby_dick);'>run: moby_dick</button><br class='clear' /></div>
|
||||
<p>
|
||||
Heredocs can be used to hold formatted or indentation-sensitive text
|
||||
(or, if you just don't feel like escaping quotes and apostrophes). The
|
||||
indentation level that begins the 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>
|
||||
<span class="String"> <strong></span>
|
||||
<span class="String"> cup of coffeescript</span>
|
||||
<span class="String"> </strong></span>
|
||||
<span class="String"> <span class="String">'</span></span><span class="String"><span class="String">'</span><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">"</span><strong><span class="UserDefinedConstant">\n</span> cup of coffeescript<span class="UserDefinedConstant">\n</span></strong><span class="String">"</span></span>;
|
||||
</pre><br class='clear' /></div>
|
||||
|
||||
<h2 id="resources">Resources</h2>
|
||||
|
||||
@@ -1231,7 +1482,7 @@ world...";
|
||||
<a href="http://github.com/jashkenas/coffee-script/">Source Code</a><br />
|
||||
After checking out the source, make sure to run <tt>rake build:parser</tt>
|
||||
to generate an up-to-date version of the Racc parser.
|
||||
Use <tt>bin/coffee</tt> to test your changes,
|
||||
Use <tt>bin/coffee</tt> to test your changes,
|
||||
<tt>rake test</tt> to run the test suite,
|
||||
and <tt>rake gem:install</tt> to
|
||||
create and install a custom version of the gem.
|
||||
@@ -1246,6 +1497,12 @@ world...";
|
||||
that includes CoffeeScript helpers,
|
||||
bundling and minification.
|
||||
</li>
|
||||
<li>
|
||||
<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="contributing">Contributing</h2>
|
||||
@@ -1281,6 +1538,36 @@ world...";
|
||||
|
||||
<h2 id="change_log">Change Log</h2>
|
||||
|
||||
<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 —
|
||||
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
|
||||
extracting values from nested arrays and objects. Added indentation-sensitive
|
||||
heredocs for nicely formatted strings or chunks of code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.3</b>
|
||||
Axed the unsatisfactory <tt>ino</tt> keyword, replacing it with <tt>of</tt> for
|
||||
object comprehensions. They now look like: <tt>for prop, value of object</tt>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.2</b>
|
||||
When performing a comprehension over an object, use <tt>ino</tt>, instead
|
||||
@@ -1311,7 +1598,7 @@ world...";
|
||||
<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.
|
||||
|
||||
@@ -10,7 +10,7 @@ require "coffee_script/parse_error"
|
||||
# Namespace for all CoffeeScript internal classes.
|
||||
module CoffeeScript
|
||||
|
||||
VERSION = '0.2.2' # Keep in sync with the gemspec.
|
||||
VERSION = '0.2.6' # Keep in sync with the gemspec.
|
||||
|
||||
# Compile a script (String or IO) to JavaScript.
|
||||
def self.compile(script, options={})
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<key>comment</key>
|
||||
<string>match stuff like: funcName: => … </string>
|
||||
<key>match</key>
|
||||
<string>([a-zA-Z0-9_?.$:*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=>)</string>
|
||||
<string>([a-zA-Z0-9_?.$:*]*?)\s*(=\b|:\b)\s*([\w,\s]*?)\s*(=+>)</string>
|
||||
<key>name</key>
|
||||
<string>meta.function.coffee</string>
|
||||
</dict>
|
||||
@@ -64,7 +64,7 @@
|
||||
<key>comment</key>
|
||||
<string>match stuff like: a => … </string>
|
||||
<key>match</key>
|
||||
<string>([a-zA-Z0-9_?., $:*]*)\s*(=>)</string>
|
||||
<string>([a-zA-Z0-9_?., $*]*)\s*(=+>)</string>
|
||||
<key>name</key>
|
||||
<string>meta.inline.function.coffee</string>
|
||||
</dict>
|
||||
@@ -93,6 +93,30 @@
|
||||
<key>name</key>
|
||||
<string>constant.numeric.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.heredoc.coffee</string>
|
||||
<key>begin</key>
|
||||
<string>("""|''')</string>
|
||||
<key>end</key>
|
||||
<string>("""|''')</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>'</string>
|
||||
@@ -206,15 +230,48 @@
|
||||
<key>name</key>
|
||||
<string>comment.line.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>(?<=[=(:]|^|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|if|return|switch|then|throw|try|unless|when|while)\b</string>
|
||||
<string>\b(break|by|catch|continue|else|finally|for|in|of|if|return|switch|then|throw|try|unless|when|while)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b([a-zA-Z$_](\w|\$|:)*)(\:)\s</string>
|
||||
<string>\b([a-zA-Z$_](\w|\$|:|\.)*\s*(?=\:))</string>
|
||||
<key>name</key>
|
||||
<string>variable.assignment.coffee</string>
|
||||
<key>captures</key>
|
||||
@@ -224,11 +281,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>
|
||||
@@ -263,7 +315,7 @@
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>!|%|&|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(in|ino|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
|
||||
<string>!|%|&|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.coffee</string>
|
||||
</dict>
|
||||
@@ -273,39 +325,6 @@
|
||||
<key>name</key>
|
||||
<string>constant.language.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>(?<=[=(:]|^|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>
|
||||
|
||||
@@ -8,7 +8,7 @@ token IDENTIFIER PROPERTY_ACCESS PROTOTYPE_ACCESS
|
||||
token CODE PARAM NEW RETURN
|
||||
token TRY CATCH FINALLY THROW
|
||||
token BREAK CONTINUE
|
||||
token FOR IN INO BY WHEN WHILE
|
||||
token FOR IN OF BY WHEN WHILE
|
||||
token SWITCH LEADING_WHEN
|
||||
token DELETE INSTANCEOF TYPEOF
|
||||
token SUPER EXTENDS
|
||||
@@ -16,6 +16,7 @@ token ARGUMENTS
|
||||
token NEWLINE
|
||||
token COMMENT
|
||||
token JS
|
||||
token THIS
|
||||
token INDENT OUTDENT
|
||||
|
||||
# Declare order of operations.
|
||||
@@ -34,12 +35,12 @@ prechigh
|
||||
left '.'
|
||||
right INDENT
|
||||
left OUTDENT
|
||||
right WHEN LEADING_WHEN IN INO BY
|
||||
right WHEN LEADING_WHEN IN OF BY
|
||||
right THROW FOR NEW SUPER
|
||||
left EXTENDS
|
||||
left ASSIGN '||=' '&&='
|
||||
left ASSIGN '||=' '&&=' '?='
|
||||
right RETURN
|
||||
right '=>' UNLESS IF ELSE WHILE
|
||||
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).
|
||||
@@ -178,6 +179,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 +188,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 +201,14 @@ rule
|
||||
|
||||
# Function definition.
|
||||
Code:
|
||||
ParamList "=>" Block { result = CodeNode.new(val[0], val[2]) }
|
||||
| "=>" Block { result = CodeNode.new([], val[1]) }
|
||||
ParamList FuncGlyph Block { result = CodeNode.new(val[0], val[2], val[1]) }
|
||||
| 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 +220,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.
|
||||
@@ -229,6 +238,7 @@ rule
|
||||
| Range { result = ValueNode.new(val[0]) }
|
||||
| Value Accessor { result = val[0] << val[1] }
|
||||
| Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
|
||||
| THIS { result = ValueNode.new(ThisNode.new) }
|
||||
;
|
||||
|
||||
# Accessing into an object or array, through dot or index notation.
|
||||
@@ -276,7 +286,6 @@ 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.
|
||||
@@ -287,7 +296,7 @@ rule
|
||||
|
||||
# Calling super.
|
||||
Super:
|
||||
SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
|
||||
SUPER "(" ArgList ")" { result = CallNode.new(Value.new('super'), val[2]) }
|
||||
;
|
||||
|
||||
# The range literal.
|
||||
@@ -315,6 +324,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 +357,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.
|
||||
@@ -361,7 +377,7 @@ rule
|
||||
# The source of the array comprehension can optionally be filtered.
|
||||
ForSource:
|
||||
IN Expression { result = {:source => val[1]} }
|
||||
| INO Expression { result = {:source => val[1], :object => true} }
|
||||
| OF Expression { result = {:source => val[1], :object => true} }
|
||||
| ForSource
|
||||
WHEN Expression { result = val[0].merge(:filter => val[2]) }
|
||||
| ForSource
|
||||
@@ -384,8 +400,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]) }
|
||||
;
|
||||
|
||||
@@ -12,31 +12,35 @@ module CoffeeScript
|
||||
"new", "return",
|
||||
"try", "catch", "finally", "throw",
|
||||
"break", "continue",
|
||||
"for", "in", "ino", "by", "where", "while",
|
||||
"for", "in", "of", "by", "where", "while",
|
||||
"delete", "instanceof", "typeof",
|
||||
"switch", "when",
|
||||
"super", "extends",
|
||||
"arguments",
|
||||
"delete", "instanceof", "typeof"]
|
||||
"this"]
|
||||
|
||||
# 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?(.*?)\n?(\s*)"{3}|'{3}\n?(.*?)\n?(\s*)'{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/
|
||||
JS_CLEANER = /(\A`|`\Z)/
|
||||
MULTILINER = /\n/
|
||||
STRING_NEWLINES = /\n\s*/
|
||||
COMMENT_CLEANER = /(^\s*#|\n\s*$)/
|
||||
NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/
|
||||
NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/
|
||||
HEREDOC_INDENT = /^\s+/
|
||||
|
||||
# Tokens which a regular expression will never immediately follow, but which
|
||||
# a division operator might.
|
||||
@@ -49,12 +53,12 @@ module CoffeeScript
|
||||
|
||||
# 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]
|
||||
while @i < @code.length
|
||||
@chunk = @code[@i..-1]
|
||||
extract_next_token
|
||||
@@ -69,6 +73,7 @@ module CoffeeScript
|
||||
def extract_next_token
|
||||
return if identifier_token
|
||||
return if number_token
|
||||
return if heredoc_token
|
||||
return if string_token
|
||||
return if js_token
|
||||
return if regex_token
|
||||
@@ -87,7 +92,7 @@ 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][1] == '.')
|
||||
@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 == '::'
|
||||
token(tag, identifier)
|
||||
@i += identifier.length
|
||||
@@ -103,14 +108,25 @@ module CoffeeScript
|
||||
# Matches strings, including multi-line strings.
|
||||
def string_token
|
||||
return false unless string = @chunk[STRING, 1]
|
||||
escaped = string.gsub(MULTILINER) do |match|
|
||||
@line += 1
|
||||
" \\\n"
|
||||
end
|
||||
escaped = string.gsub(STRING_NEWLINES, " \\\n")
|
||||
token(:STRING, escaped)
|
||||
@line += string.count("\n")
|
||||
@i += string.length
|
||||
end
|
||||
|
||||
# Matches heredocs, adjusting indentation to the correct level.
|
||||
def heredoc_token
|
||||
return false unless match = @chunk.match(HEREDOC)
|
||||
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")
|
||||
@i += match[1].length
|
||||
end
|
||||
|
||||
# Matches interpolated JavaScript.
|
||||
def js_token
|
||||
return false unless script = @chunk[JS, 1]
|
||||
@@ -141,7 +157,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) && !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
|
||||
|
||||
@@ -20,32 +20,31 @@
|
||||
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every
|
||||
// command.
|
||||
exports.run = function run(args) {
|
||||
var __a, __b, i, result;
|
||||
var __a, __b, i, path, result;
|
||||
if (args.length) {
|
||||
__a = args;
|
||||
__b = function(path, i) {
|
||||
for (i = 0; i < __a.length; i++) {
|
||||
path = __a[i];
|
||||
exports.evalCS(File.read(path));
|
||||
delete args[i];
|
||||
};
|
||||
if (__a instanceof Array) {
|
||||
for (i=0; i<__a.length; i++) __b(__a[i], i);
|
||||
} else {
|
||||
for (i in __a) { if (__a.hasOwnProperty(i)) __b(__a[i], 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);
|
||||
}
|
||||
})());
|
||||
}
|
||||
return null;
|
||||
return __b;
|
||||
};
|
||||
// Compile a given CoffeeScript file into JavaScript.
|
||||
exports.compileFile = function compileFile(path) {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
module CoffeeScript
|
||||
|
||||
# The abstract base class for all CoffeeScript nodes.
|
||||
# All nodes are implement a "compile_node" method, which performs the
|
||||
# code generation for that node. To compile a node, call the "compile"
|
||||
# method, which wraps "compile_node" in some extra smarts, to know when the
|
||||
# generated code should be wrapped up in a closure. An options hash is passed
|
||||
# and cloned throughout, containing messages from higher in the AST,
|
||||
# information about the current scope, and indentation level.
|
||||
class Node
|
||||
# Tabs are two spaces for pretty-printing.
|
||||
TAB = ' '
|
||||
@@ -18,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
|
||||
@@ -30,34 +49,50 @@ 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
|
||||
|
||||
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]
|
||||
@indent = (o[:indent] = idt(1))
|
||||
pass_this = !o[:closure] && contains? {|node| node.is_a?(ThisNode) }
|
||||
param = pass_this ? '__this' : ''
|
||||
body = compile_node(o.merge(:return => true, :closure => true))
|
||||
"(function(#{param}) {\n#{body}\n#{indent}})(#{pass_this ? 'this' : ''})"
|
||||
end
|
||||
|
||||
# Quick method for the current indentation level, plus tabs out.
|
||||
# Quick short method for the current indentation level, plus tabbing in.
|
||||
def idt(tabs=0)
|
||||
@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
|
||||
|
||||
STRIP_TRAILING_WHITESPACE = /\s+$/
|
||||
TRAILING_WHITESPACE = /\s+$/
|
||||
|
||||
# Wrap up a node as an Expressions, unless it already is.
|
||||
def self.wrap(*nodes)
|
||||
@@ -69,12 +104,13 @@ module CoffeeScript
|
||||
@expressions = nodes.flatten
|
||||
end
|
||||
|
||||
# Tack an expression onto the end of this node.
|
||||
# Tack an expression on to the end of this expression list.
|
||||
def <<(node)
|
||||
@expressions << node
|
||||
self
|
||||
end
|
||||
|
||||
# Tack an expression on to the beginning of this expression list.
|
||||
def unshift(node)
|
||||
@expressions.unshift(node)
|
||||
self
|
||||
@@ -85,6 +121,11 @@ 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
|
||||
@@ -95,49 +136,48 @@ module CoffeeScript
|
||||
o[:scope] ? super(o) : compile_root(o)
|
||||
end
|
||||
|
||||
# The extra fancy is to handle pushing down returns to the final lines of
|
||||
# inner statements. Variables first defined within the Expressions body
|
||||
# have their declarations pushed up top of the closest scope.
|
||||
# Compile each expression in the Expressions body.
|
||||
def compile_node(options={})
|
||||
compiled = @expressions.map do |node|
|
||||
o = options.dup
|
||||
@indent = o[:indent]
|
||||
returns = o.delete(:return)
|
||||
if last?(node) && returns && !node.statement_only?
|
||||
if node.statement?
|
||||
node.compile(o.merge(:return => true))
|
||||
else
|
||||
if o[:top] && o[:last_assign] && o[:last_assign][0..0][/[A-Z]/]
|
||||
temp = o[:scope].free_variable
|
||||
"#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:last_assign]} === this.constructor ? this : #{temp};"
|
||||
else
|
||||
"#{idt}return #{node.compile(o)};"
|
||||
end
|
||||
end
|
||||
else
|
||||
ending = node.statement? ? '' : ';'
|
||||
indent = node.statement? ? '' : idt
|
||||
"#{indent}#{node.compile(o.merge(:top => true))}#{ending}"
|
||||
end
|
||||
end
|
||||
write(compiled.join("\n"))
|
||||
write(@expressions.map {|n| compile_expression(n, options.dup) }.join("\n"))
|
||||
end
|
||||
|
||||
# If this is the top-level Expressions, wrap everything in a safety closure.
|
||||
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!(STRIP_TRAILING_WHITESPACE, '')
|
||||
o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
|
||||
code.gsub!(TRAILING_WHITESPACE, '')
|
||||
write(o[:no_wrap] ? code : "(function(){\n#{code}\n})();")
|
||||
end
|
||||
|
||||
# Compile the expressions body, with declarations of all inner variables
|
||||
# at the top.
|
||||
def compile_with_declarations(o={})
|
||||
code = compile_node(o)
|
||||
decls = ''
|
||||
decls = "#{idt}var #{o[:scope].declared_variables.join(', ')};\n" if o[:scope].declarations?(self)
|
||||
decls + 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.
|
||||
def compile_expression(node, o)
|
||||
@indent = o[:indent]
|
||||
stmt = node.statement?
|
||||
# We need to return the result if this is the last node in the expressions body.
|
||||
returns = o.delete(:return) && last?(node) && !node.statement_only?
|
||||
# Return the regular compile of the node, unless we need to return the result.
|
||||
return "#{stmt ? '' : idt}#{node.compile(o.merge(:top => true))}#{stmt ? '' : ';'}" unless returns
|
||||
# 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 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[:scope].function.name} === this.constructor ? this : #{temp};"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -145,14 +185,13 @@ 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']
|
||||
|
||||
CONVERSIONS = {
|
||||
'arguments' => '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))
|
||||
end
|
||||
@@ -166,19 +205,21 @@ module CoffeeScript
|
||||
end
|
||||
alias_method :statement_only?, :statement?
|
||||
|
||||
def arguments?
|
||||
@value.to_s == 'arguments'
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
val = CONVERSIONS[@value.to_s] || @value.to_s
|
||||
indent = statement? ? idt : ''
|
||||
ending = statement? ? ';' : ''
|
||||
write("#{indent}#{val}#{ending}")
|
||||
"#{indent}#{@value}#{ending}"
|
||||
end
|
||||
end
|
||||
|
||||
# Try to return your expression, or tell it to return itself.
|
||||
# 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
|
||||
@@ -194,7 +235,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
|
||||
@@ -202,8 +243,7 @@ module CoffeeScript
|
||||
|
||||
def compile_node(o={})
|
||||
delimiter = "\n#{idt}//"
|
||||
comment = "#{delimiter}#{@lines.join(delimiter)}"
|
||||
write(comment)
|
||||
write("#{delimiter}#{@lines.join(delimiter)}")
|
||||
end
|
||||
|
||||
end
|
||||
@@ -211,70 +251,73 @@ 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
|
||||
|
||||
# Compile a function call being passed variable arguments.
|
||||
def compile_splat(o)
|
||||
meth = @variable.compile(o)
|
||||
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
|
||||
end
|
||||
|
||||
# Hooking one constructor into another's prototype chain.
|
||||
def compile_node(o={})
|
||||
constructor = o[:scope].free_variable
|
||||
sub, sup = @sub_object.compile(o), @super_object.compile(o)
|
||||
@@ -289,10 +332,11 @@ module CoffeeScript
|
||||
|
||||
# A value, indexed or dotted into, or vanilla.
|
||||
class ValueNode < Node
|
||||
attr_reader :literal, :properties, :last, :source
|
||||
children :base, :properties
|
||||
attr_reader :last, :source
|
||||
|
||||
def initialize(literal, properties=[])
|
||||
@literal, @properties = literal, properties
|
||||
def initialize(base, properties=[])
|
||||
@base, @properties = base, properties
|
||||
end
|
||||
|
||||
def <<(other)
|
||||
@@ -304,25 +348,41 @@ module CoffeeScript
|
||||
return !@properties.empty?
|
||||
end
|
||||
|
||||
def array?
|
||||
@base.is_a?(ArrayNode) && !properties?
|
||||
end
|
||||
|
||||
def object?
|
||||
@base.is_a?(ObjectNode) && !properties?
|
||||
end
|
||||
|
||||
def splice?
|
||||
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?
|
||||
@literal.is_a?(Node) && @literal.statement? && !properties?
|
||||
@base.is_a?(Node) && @base.statement? && !properties?
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
only = o.delete(:only_first)
|
||||
props = only ? @properties[0...-1] : @properties
|
||||
parts = [@literal, props].flatten.map do |val|
|
||||
val.respond_to?(:compile) ? val.compile(o) : val.to_s
|
||||
end
|
||||
parts = [@base, props].flatten.map {|val| val.compile(o) }
|
||||
@last = parts.last
|
||||
@source = parts.length > 1 ? parts[0...-1].join('') : nil
|
||||
write(parts.join(''))
|
||||
end
|
||||
end
|
||||
|
||||
# A dotted accessor into a part of a value.
|
||||
# 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
|
||||
|
||||
def initialize(name, prototype=false)
|
||||
@name, @prototype = name, prototype
|
||||
@@ -336,7 +396,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
|
||||
@@ -347,10 +407,20 @@ module CoffeeScript
|
||||
end
|
||||
end
|
||||
|
||||
# A node to represent a reference to "this". Needs to be transformed into a
|
||||
# reference to the correct value of "this", when used within a closure wrapper.
|
||||
class ThisNode < Node
|
||||
|
||||
def compile_node(o)
|
||||
write(o[:closure] ? "__this" : "this")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# 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
|
||||
@@ -360,14 +430,6 @@ module CoffeeScript
|
||||
@exclusive
|
||||
end
|
||||
|
||||
def less_operator
|
||||
@exclusive ? '<' : '<='
|
||||
end
|
||||
|
||||
def greater_operator
|
||||
@exclusive ? '>' : '>='
|
||||
end
|
||||
|
||||
def compile_variables(o)
|
||||
@indent = o[:indent]
|
||||
@from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
|
||||
@@ -376,12 +438,13 @@ module CoffeeScript
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
return compile_array(o) unless o[:index]
|
||||
idx, step = o.delete(:index), o.delete(:step)
|
||||
return compile_array(o) unless idx
|
||||
vars = "#{idx}=#{@from_var}"
|
||||
step = step ? step.compile(o) : '1'
|
||||
compare = "(#{@from_var} <= #{@to_var} ? #{idx} #{less_operator} #{@to_var} : #{idx} #{greater_operator} #{@to_var})"
|
||||
incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
|
||||
vars = "#{idx}=#{@from_var}"
|
||||
step = step ? step.compile(o) : '1'
|
||||
equals = @exclusive ? '' : '='
|
||||
compare = "(#{@from_var} <= #{@to_var} ? #{idx} <#{equals} #{@to_var} : #{idx} >#{equals} #{@to_var})"
|
||||
incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
|
||||
write("#{vars}; #{compare}; #{incr}")
|
||||
end
|
||||
|
||||
@@ -400,7 +463,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
|
||||
@@ -416,26 +479,63 @@ 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_splice(o) if @variable.properties.last.is_a?(SliceNode)
|
||||
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)}"
|
||||
write(o[:return] ? "#{idt}return (#{val})" : val)
|
||||
return write("#{idt}#{val};") if stmt
|
||||
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?
|
||||
value? && (@variable.array? || @variable.object?)
|
||||
end
|
||||
|
||||
# Implementation of recursive pattern matching, when assigning array or
|
||||
# object literals to a value. Peeks at their properties to assign inner names.
|
||||
# See: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring
|
||||
def compile_pattern_match(o)
|
||||
val_var = o[:scope].free_variable
|
||||
assigns = ["#{idt}#{val_var} = #{@value.compile(o)};"]
|
||||
o.merge!(:top => true, :as_statement => true)
|
||||
@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
|
||||
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
|
||||
|
||||
def compile_splice(o)
|
||||
@@ -451,6 +551,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 = {
|
||||
:== => "===",
|
||||
:'!=' => "!==",
|
||||
@@ -460,11 +564,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
|
||||
@@ -474,18 +577,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)]
|
||||
@@ -495,66 +619,80 @@ 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)
|
||||
o.delete(:closure)
|
||||
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 = [])
|
||||
@properties = properties
|
||||
@@ -580,7 +718,7 @@ module CoffeeScript
|
||||
|
||||
# An array literal.
|
||||
class ArrayNode < Node
|
||||
attr_reader :objects
|
||||
children :objects
|
||||
|
||||
def initialize(objects=[])
|
||||
@objects = objects
|
||||
@@ -598,25 +736,45 @@ 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 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
|
||||
|
||||
@@ -625,10 +783,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]
|
||||
@@ -640,15 +799,15 @@ module CoffeeScript
|
||||
|
||||
def compile_node(o)
|
||||
top_level = o.delete(:top) && !o[:return]
|
||||
range = @source.is_a?(ValueNode) && @source.literal.is_a?(RangeNode) && @source.properties.empty?
|
||||
source = range ? @source.literal : @source
|
||||
range = @source.is_a?(ValueNode) && @source.base.is_a?(RangeNode) && @source.properties.empty?
|
||||
source = range ? @source.base : @source
|
||||
scope = o[:scope]
|
||||
name_found = @name && scope.find(@name)
|
||||
index_found = @index && scope.find(@index)
|
||||
body_dent = idt(1)
|
||||
rvar = scope.free_variable unless top_level
|
||||
svar = scope.free_variable
|
||||
ivar = range ? name : @index ? @index : scope.free_variable
|
||||
rvar = scope.free_variable unless top_level
|
||||
if range
|
||||
index_var = scope.free_variable
|
||||
source_part = source.compile_variables(o)
|
||||
@@ -657,8 +816,7 @@ module CoffeeScript
|
||||
else
|
||||
index_var = nil
|
||||
source_part = "#{svar} = #{source.compile(o)};\n#{idt}"
|
||||
for_part = "#{ivar}=0; #{ivar}<#{svar}.length; #{ivar}++"
|
||||
for_part = "#{ivar} in #{svar}" if @object
|
||||
for_part = @object ? "#{ivar} in #{svar}" : "#{ivar} = 0; #{ivar} < #{svar}.length; #{ivar}++"
|
||||
var_part = @name ? "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" : ''
|
||||
end
|
||||
body = @body
|
||||
@@ -667,9 +825,7 @@ module CoffeeScript
|
||||
if top_level
|
||||
body = Expressions.wrap(body)
|
||||
else
|
||||
body = Expressions.wrap(CallNode.new(
|
||||
ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [body.unwrap]
|
||||
))
|
||||
body = PushNode.wrap(rvar, body)
|
||||
end
|
||||
if o[:return]
|
||||
return_result = "return #{return_result}" if o[:return]
|
||||
@@ -679,11 +835,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
|
||||
|
||||
@@ -696,10 +854,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
|
||||
@@ -716,10 +874,9 @@ module CoffeeScript
|
||||
|
||||
# Throw an exception.
|
||||
class ThrowNode < Node
|
||||
children :expression
|
||||
statement_only
|
||||
|
||||
attr_reader :expression
|
||||
|
||||
def initialize(expression)
|
||||
@expression = expression
|
||||
end
|
||||
@@ -731,15 +888,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
|
||||
|
||||
@@ -747,7 +909,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
|
||||
@@ -766,13 +928,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
|
||||
|
||||
@@ -794,7 +957,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
|
||||
@@ -816,6 +980,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
|
||||
@@ -831,7 +999,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))}" :
|
||||
|
||||
@@ -5,6 +5,12 @@ module CoffeeScript
|
||||
# line-number aware.
|
||||
class ParseError < Racc::ParseError
|
||||
|
||||
TOKEN_MAP = {
|
||||
'INDENT' => 'indent',
|
||||
'OUTDENT' => 'outdent',
|
||||
"\n" => 'newline'
|
||||
}
|
||||
|
||||
def initialize(token_id, value, stack)
|
||||
@token_id, @value, @stack = token_id, value, stack
|
||||
end
|
||||
@@ -13,7 +19,7 @@ module CoffeeScript
|
||||
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}'"
|
||||
val_part = " for #{TOKEN_MAP[@value.to_s] || "'#{@value}'"}"
|
||||
"#{line_part} syntax error#{val_part}#{id_part}"
|
||||
end
|
||||
alias_method :inspect, :message
|
||||
|
||||
@@ -26,7 +26,7 @@ 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_LINERS = [:ELSE, "=>", "==>", :TRY, :FINALLY, :THEN]
|
||||
SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN]
|
||||
|
||||
# Rewrite the token stream in multiple passes, one logical filter at
|
||||
@@ -35,6 +35,7 @@ module CoffeeScript
|
||||
def rewrite(tokens)
|
||||
@tokens = tokens
|
||||
adjust_comments
|
||||
remove_leading_newlines
|
||||
remove_mid_expression_newlines
|
||||
move_commas_outside_outdents
|
||||
add_implicit_indentation
|
||||
@@ -82,6 +83,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,6 +20,10 @@ module CoffeeScript
|
||||
to_str.to_sym
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
to_s
|
||||
end
|
||||
|
||||
def inspect
|
||||
@value.inspect
|
||||
end
|
||||
@@ -37,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
|
||||
@@ -5,5 +5,5 @@
|
||||
"description": "Unfancy JavaScript",
|
||||
"keywords": ["javascript", "language"],
|
||||
"author": "Jeremy Ashkenas",
|
||||
"version": "0.2.2"
|
||||
"version": "0.2.6"
|
||||
}
|
||||
|
||||
@@ -22,3 +22,11 @@ curried: =>
|
||||
print(area.apply(this, arguments.concat(20, 20)) is 100)
|
||||
|
||||
curried(10, 10)
|
||||
|
||||
|
||||
# Arguments is not a special keyword -- it can be assigned to:
|
||||
func: =>
|
||||
arguments: 25
|
||||
arguments
|
||||
|
||||
print(func(100) is 25)
|
||||
|
||||
@@ -5,8 +5,8 @@ print(results.join(',') is '2,18')
|
||||
|
||||
|
||||
obj: {one: 1, two: 2, three: 3}
|
||||
names: key + '!' for key ino obj
|
||||
odds: key + '!' for key, value ino obj when value % 2 isnt 0
|
||||
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!")
|
||||
|
||||
@@ -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)
|
||||
23
test/fixtures/execution/test_assignment.coffee
vendored
Normal file
23
test/fixtures/execution/test_assignment.coffee
vendored
Normal 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)
|
||||
62
test/fixtures/execution/test_destructuring_assignment.coffee
vendored
Normal file
62
test/fixtures/execution/test_destructuring_assignment.coffee
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
a: -1
|
||||
b: -2
|
||||
|
||||
[a, b]: [b, a]
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
person: {
|
||||
name: "Bob"
|
||||
family: {
|
||||
brother: {
|
||||
addresses: [
|
||||
"first"
|
||||
{
|
||||
street: "101 Deercreek Ln."
|
||||
city: "Moquasset NY, 10021"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{name: a, family: {brother: {addresses: [one, {city: b}]}}}: person
|
||||
|
||||
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")
|
||||
@@ -26,4 +26,4 @@ func: =>
|
||||
|
||||
c.single: c.list[1..1][0]
|
||||
|
||||
print(func() == '-')
|
||||
print(func() is '-')
|
||||
|
||||
30
test/fixtures/execution/test_existence.coffee
vendored
30
test/fixtures/execution/test_existence.coffee
vendored
@@ -2,4 +2,32 @@ 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)
|
||||
30
test/fixtures/execution/test_expressions.coffee
vendored
Normal file
30
test/fixtures/execution/test_expressions.coffee
vendored
Normal 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)
|
||||
48
test/fixtures/execution/test_functions.coffee
vendored
Normal file
48
test/fixtures/execution/test_functions.coffee
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
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)
|
||||
37
test/fixtures/execution/test_heredocs.coffee
vendored
Normal file
37
test/fixtures/execution/test_heredocs.coffee
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
a: """
|
||||
basic heredoc
|
||||
on two lines
|
||||
"""
|
||||
|
||||
print(a is "basic heredoc\non two lines")
|
||||
|
||||
|
||||
a: '''
|
||||
a
|
||||
"b
|
||||
c
|
||||
'''
|
||||
|
||||
print(a is "a\n \"b\nc")
|
||||
|
||||
|
||||
a: '''one-liner'''
|
||||
|
||||
print(a is 'one-liner')
|
||||
|
||||
|
||||
a: """
|
||||
out
|
||||
here
|
||||
"""
|
||||
|
||||
print(a is "out\nhere")
|
||||
|
||||
|
||||
a: '''
|
||||
a
|
||||
b
|
||||
c
|
||||
'''
|
||||
|
||||
print(a is " a\n b\nc")
|
||||
@@ -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')
|
||||
18
test/fixtures/execution/test_operations.coffee
vendored
Normal file
18
test/fixtures/execution/test_operations.coffee
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# CoffeeScript's operations should be chainable, like Python's.
|
||||
|
||||
print(500 > 50 > 5 > -5)
|
||||
|
||||
print(true is not false is true is not false)
|
||||
|
||||
print(10 < 20 > 10)
|
||||
|
||||
print(50 > 10 > 5 is parseInt('5', 10))
|
||||
|
||||
|
||||
# Make sure that each argument is only evaluated once, even if used
|
||||
# more than once.
|
||||
|
||||
i: 0
|
||||
func: => i++
|
||||
|
||||
print(1 > func() < 1)
|
||||
2
test/fixtures/execution/test_splices.coffee
vendored
2
test/fixtures/execution/test_splices.coffee
vendored
@@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
|
||||
array[5..10]: [0, 0, 0]
|
||||
|
||||
14
test/fixtures/execution/test_switch.coffee
vendored
14
test/fixtures/execution/test_switch.coffee
vendored
@@ -15,3 +15,17 @@ result: switch num
|
||||
else false
|
||||
|
||||
print(result)
|
||||
|
||||
|
||||
func: num =>
|
||||
switch num
|
||||
when 2, 4, 6
|
||||
true
|
||||
when 1, 3, 5
|
||||
false
|
||||
else false
|
||||
|
||||
print(func(2))
|
||||
print(func(6))
|
||||
print(!func(3))
|
||||
print(!func(8))
|
||||
|
||||
17
test/fixtures/execution/test_while.coffee
vendored
Normal file
17
test/fixtures/execution/test_while.coffee
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
i: 100
|
||||
while i -= 1
|
||||
|
||||
print(i is 0)
|
||||
|
||||
|
||||
i: 5
|
||||
list: while i -= 1
|
||||
i * 2
|
||||
|
||||
print(list.join(' ') is "8 6 4 2")
|
||||
|
||||
|
||||
i: 5
|
||||
list: (i * 3 while i -= 1)
|
||||
|
||||
print(list.join(' ') is "12 9 6 3")
|
||||
@@ -4,12 +4,16 @@ class ExecutionTest < Test::Unit::TestCase
|
||||
|
||||
NO_WARNINGS = "0 error(s), 0 warning(s)"
|
||||
|
||||
SOURCES = [
|
||||
'test/fixtures/execution/*.coffee',
|
||||
'examples/beautiful_code/*.coffee'
|
||||
]
|
||||
|
||||
# This is by far the most important test. It evaluates all of the
|
||||
# CoffeeScript in test/fixtures/execution, ensuring that all our
|
||||
# syntax actually works.
|
||||
# CoffeeScript in test/fixtures/execution, as well as examples/beautiful_code,
|
||||
# ensuring that all our syntax actually works.
|
||||
def test_execution_of_coffeescript
|
||||
sources = ['test/fixtures/execution/*.coffee'].join(' ')
|
||||
(`bin/coffee -r #{sources}`).split("\n").each do |line|
|
||||
(`bin/coffee -r #{SOURCES.join(' ')}`).split("\n").each do |line|
|
||||
assert line == "true"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,14 +7,14 @@ class LexerTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_lexing_an_empty_string
|
||||
assert @lex.tokenize("") == [["\n", "\n"]]
|
||||
assert @lex.tokenize("") == []
|
||||
end
|
||||
|
||||
def test_lexing_basic_assignment
|
||||
code = "a: 'one'\nb: [1, 2]"
|
||||
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"],
|
||||
[:STRING, "'one'"], ["\n", "\n"], [:IDENTIFIER, "b"], [:ASSIGN, ":"],
|
||||
["[", "["], [:NUMBER, "1"], [",", ","], [:NUMBER, "2"], ["]", "]"],
|
||||
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"],
|
||||
[:STRING, "'one'"], ["\n", "\n"], [:IDENTIFIER, "b"], [:ASSIGN, ":"],
|
||||
["[", "["], [:NUMBER, "1"], [",", ","], [:NUMBER, "2"], ["]", "]"],
|
||||
["\n", "\n"]]
|
||||
end
|
||||
|
||||
@@ -27,7 +27,7 @@ class LexerTest < Test::Unit::TestCase
|
||||
def test_lexing_function_definition
|
||||
code = "x, y => x * y"
|
||||
assert @lex.tokenize(code) == [[:PARAM, "x"], [",", ","], [:PARAM, "y"],
|
||||
["=>", "=>"], [:INDENT, 2], [:IDENTIFIER, "x"], ["*", "*"],
|
||||
["=>", "=>"], [:INDENT, 2], [:IDENTIFIER, "x"], ["*", "*"],
|
||||
[:IDENTIFIER, "y"], [:OUTDENT, 2], ["\n", "\n"]]
|
||||
end
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@ class ParserTest < Test::Unit::TestCase
|
||||
assert nodes.length == 1
|
||||
assign = nodes.first
|
||||
assert assign.is_a?(AssignNode)
|
||||
assert assign.variable.literal == 'a'
|
||||
assert assign.variable.base == 'a'
|
||||
end
|
||||
|
||||
def test_parsing_an_object_literal
|
||||
nodes = @par.parse("{one : 1\ntwo : 2}").expressions
|
||||
obj = nodes.first.literal
|
||||
obj = nodes.first.base
|
||||
assert obj.is_a?(ObjectNode)
|
||||
assert obj.properties.first.variable.literal.value == "one"
|
||||
assert obj.properties.last.variable.literal.value == "two"
|
||||
assert obj.properties.first.variable.base.value == "one"
|
||||
assert obj.properties.last.variable.base.value == "two"
|
||||
end
|
||||
|
||||
def test_parsing_an_function_definition
|
||||
@@ -39,17 +39,17 @@ class ParserTest < Test::Unit::TestCase
|
||||
def test_parsing_if_statement
|
||||
the_if = @par.parse("clap_your_hands() if happy").expressions.first
|
||||
assert the_if.is_a?(IfNode)
|
||||
assert the_if.condition.literal == 'happy'
|
||||
assert the_if.condition.base == 'happy'
|
||||
assert the_if.body.is_a?(CallNode)
|
||||
assert the_if.body.variable.literal == 'clap_your_hands'
|
||||
assert the_if.body.variable.base == 'clap_your_hands'
|
||||
end
|
||||
|
||||
def test_parsing_array_comprehension
|
||||
nodes = @par.parse("i for x, i in [10, 9, 8, 7, 6, 5] when i % 2 is 0").expressions
|
||||
assert nodes.first.is_a?(ForNode)
|
||||
assert nodes.first.body.literal == 'i'
|
||||
assert nodes.first.body.base == 'i'
|
||||
assert nodes.first.filter.operator == '==='
|
||||
assert nodes.first.source.literal.objects.last.literal.value == "5"
|
||||
assert nodes.first.source.base.objects.last.base.value == "5"
|
||||
end
|
||||
|
||||
def test_parsing_comment
|
||||
@@ -66,7 +66,7 @@ class ParserTest < Test::Unit::TestCase
|
||||
nodes = @par.parse(File.read('test/fixtures/generation/each.coffee'))
|
||||
assign = nodes.expressions[1]
|
||||
assert assign.is_a?(AssignNode)
|
||||
assert assign.variable.literal == '_'
|
||||
assert assign.variable.base == '_'
|
||||
assert assign.value.is_a?(CodeNode)
|
||||
assert assign.value.params == ['obj', 'iterator', 'context']
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user