Compare commits

...

33 Commits
0.2.2 ... 0.2.5

Author SHA1 Message Date
Jeremy Ashkenas
e77e520607 CoffeeScript 0.2.5 is on the books 2010-01-13 23:24:45 -05:00
Jeremy Ashkenas
ed8a54995d with splats allowed in destructuring assignment 2010-01-13 22:25:58 -05:00
Jeremy Ashkenas
2d206e7b60 pulling out pushes into a pushnode 2010-01-13 21:33:46 -05:00
Jeremy Ashkenas
bb9fdd3015 while loops can now be used as expressions -- they return an array containing the computed result of each iteration. 2010-01-13 21:27:22 -05:00
Jeremy Ashkenas
1e7d638435 adding bound functions, with test 2010-01-13 20:59:57 -05:00
Jeremy Ashkenas
0ceca0778c adding when clauses with multiple values 2010-01-13 19:56:35 -05:00
Jeremy Ashkenas
abd9ab5c71 unified ParamSplatNode and ArgSplatNode into SplatNode 2010-01-12 23:49:47 -05:00
Jeremy Ashkenas
ea349a1a59 more safety type-checks in nodes.rb 2010-01-12 23:26:35 -05:00
Jeremy Ashkenas
f0d5db7e66 fixing heredocs to use the left-most indent as the indentation guide -- not just the first line of the heredoc 2010-01-12 23:06:12 -05:00
Jeremy Ashkenas
914ba1c244 removing commented-out bit 2010-01-12 18:01:12 -05:00
Jeremy Ashkenas
844ea33274 mistaken commit 2010-01-12 17:45:06 -05:00
Jeremy Ashkenas
87e04e9952 nicer syntax error messages for newlines and indentation 2010-01-12 17:44:37 -05:00
Jeremy Ashkenas
197914bcf7 nicer syntax error messages for newlines and indentation 2010-01-12 17:44:03 -05:00
Jeremy Ashkenas
8dfbd1a2a8 using Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty, with an alias, for Rhino and java objects 2010-01-12 17:35:37 -05:00
Jeremy Ashkenas
c19647ad33 adding and fixing test for empty strings 2010-01-12 08:52:44 -05:00
Jeremy Ashkenas
27f7ef09af allow leading newlines in coffee scripts 2010-01-12 08:49:39 -05:00
Jeremy Ashkenas
9a61bbf005 CoffeeScript 0.2.4, with pattern matching and heredocs 2010-01-12 00:09:23 -05:00
Jeremy Ashkenas
c8d505e85d heredoc docs 2010-01-12 00:00:38 -05:00
Jeremy Ashkenas
477c510345 adding heredocs, with tests 2010-01-11 23:53:50 -05:00
Jeremy Ashkenas
c3029faca7 documentation for pattern matching 2010-01-11 22:55:01 -05:00
Jeremy Ashkenas
186797a745 got compile_pattern_match about as small as its going to get 2010-01-11 22:16:23 -05:00
Jeremy Ashkenas
d54fa2f2a1 a whole chunk of compile_pattern_match was redundant, axed it -- along with the array/vs/object split 2010-01-11 22:12:18 -05:00
Jeremy Ashkenas
5e1e949bf6 a passing test for destructuring assignment (it needs a better name) 2010-01-11 22:04:25 -05:00
Jeremy Ashkenas
6c980d8adc first draft of destructuring assignment -- working out the wrinkles -- not sure if we want to do the full spec 2010-01-11 21:44:47 -05:00
Jeremy Ashkenas
2f63439bff swapping around the order of variable declaration in array comprehensions, so that it comes out in the proper order: __a, __b, __c 2010-01-11 09:16:08 -05:00
Jeremy Ashkenas
c7cb308b6d adding note about parens-around-ambiguous-function-defs to the docs 2010-01-11 09:09:06 -05:00
Jeremy Ashkenas
9cc7d6af27 little lexer tweak 2010-01-11 08:46:50 -05:00
Jeremy Ashkenas
cfa357cbc3 CoffeeScript 0.2.3, with 'of', not 'ino' 2010-01-11 00:01:16 -05:00
Jeremy Ashkenas
9d8668f37f added a whole slew of nice Potion examples from the pamphlet -- CoffeeScript stacks up pretty well. 2010-01-10 23:27:57 -05:00
Jeremy Ashkenas
d1ddeacbe3 more refactors to nodes 2010-01-10 22:35:55 -05:00
Jeremy Ashkenas
d9d09a9a72 refactoring and commenting nodes.rb Expressions 2010-01-10 22:04:38 -05:00
Jeremy Ashkenas
a1528f3f19 rebuilt the narwhal interface 2010-01-10 20:25:09 -05:00
Jeremy Ashkenas
7d2a955e0a bumping the license to 2010 2010-01-10 17:57:29 -05:00
52 changed files with 1338 additions and 399 deletions

View File

@@ -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

View File

@@ -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.5' # Keep version in sync with coffee-script.rb
s.date = '2010-1-13'
s.homepage = "http://jashkenas.github.com/coffee-script/"
s.summary = "The CoffeeScript Compiler"

View File

@@ -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]

View File

@@ -0,0 +1,5 @@
html: '''
<strong>
cup of coffeescript
</strong>
'''

View File

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

View File

@@ -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")

View File

@@ -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

View 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

View File

@@ -0,0 +1,4 @@
bait: 1000
and_switch: 0
[bait, and_switch]: [and_switch, bait]

View File

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

View File

@@ -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."

View File

@@ -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.5</a>
</p>
<h2>Table of Contents</h2>
@@ -75,10 +75,12 @@
<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="#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 +97,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.
@@ -386,9 +388,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 +420,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(", ")') %>
@@ -481,12 +486,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 +504,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 +561,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">
@@ -527,10 +576,17 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
<%= code_for('try') %>
<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 +595,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.
@@ -589,6 +645,28 @@ 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.5</b>
The conditions in switch statements can now take multiple values at once &mdash;
If any of them are true, the case will run. Added the long arrow <tt>==></tt>,
which defines and immediately binds a function to <tt>this</tt>. While loops can
now be used as expressions, in the same way that comprehensions can. Splats
can be used within pattern matches to soak up the rest of an array.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.2.4</b>
Added ECMAScript Harmony style destructuring assignment, for dealing with
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

View File

@@ -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)) {

View File

@@ -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);
})();

View File

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

View File

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

View 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];
})();

View File

@@ -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;
})();
})();

View 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];
})();

View File

@@ -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;
})();
})();

View 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];
})();

View File

@@ -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;
};
})();

View File

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

View File

@@ -1,9 +1,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;
})();
})();

View File

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

View File

@@ -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">=&gt;</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">=&gt;</span>
<span class="line-numbers"> 150 </span> <span class="Keyword">return</span> _.indexOf(obj, target) <span class="Keyword">isnt</span> <span class="Keyword">-</span><span class="Number">1</span> <span class="Keyword">if</span> _.isArray(obj)
<span class="line-numbers"> 151 </span> <span class="Keyword">for</span> key, val <span class="Keyword">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">=&gt;</span>
<span class="line-numbers"> 382 </span> <span class="Keyword">return</span> _.range(<span class="Number">0</span>, obj.length) <span class="Keyword">if</span> _.isArray(obj)
<span class="line-numbers"> 383 </span> key <span class="Keyword">for</span> key, val <span class="Keyword">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">=&gt;</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">=&gt;</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">=&gt;</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>

View File

@@ -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
View 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?

View File

@@ -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

View File

@@ -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.5</a>
</p>
<h2>Table of Contents</h2>
@@ -61,10 +61,12 @@
<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="#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 +139,12 @@ race <span class="Keyword">=</span> <span class="Storage">function</span> <span
}
<span class="Comment"><span class="Comment">//</span> Array comprehensions:</span>
cubed_list <span class="Keyword">=</span> (<span class="Storage">function</span>() {
__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">&lt;</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">&lt;</span> __b.<span class="LibraryConstant">length</span>; __c<span class="Keyword">++</span>) {
num <span class="Keyword">=</span> __b[__c];
__a.<span class="LibraryFunction">push</span>(math.cube(num));
}
<span class="Keyword">return</span> __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 +180,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 +194,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.
@@ -666,21 +668,56 @@ backwards("stairway", "to", "heaven");
<p id="while">
<b class="header">While Loops</b>
The only low-level loop that CoffeeScript provides is the while loop.
The only low-level loop that CoffeeScript provides is the <b>while</b> loop. The
main difference from JavaScript is that the <b>while</b> loop can be used
as an expression, returning an array containing the result of each iteration
through the loop.
</p>
<div class='code'><pre class="idle"><span class="Keyword">while</span> demand <span class="Keyword">&gt;</span> supply
sell()
restock()
<div class='code'><pre class="idle"><span class="Keyword">if</span> <span class="Variable">this</span>.studying_economics
<span class="Keyword">while</span> supply <span class="Keyword">&gt;</span> demand <span class="Keyword">then</span> buy()
<span class="Keyword">while</span> supply <span class="Keyword">&lt;</span> demand <span class="Keyword">then</span> sell()
<span class="Keyword">while</span> supply <span class="Keyword">&gt;</span> demand <span class="Keyword">then</span> buy()
</pre><pre class="idle"><span class="Keyword">while</span> (demand <span class="Keyword">&gt;</span> supply) {
sell();
restock();
<span class="FunctionName">num</span><span class="Keyword">:</span> <span class="Number">6</span>
<span class="FunctionName">lyrics</span><span class="Keyword">:</span> <span class="Keyword">while</span> num <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">1</span>
num <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> little monkeys, jumping on the bed.</span>
<span class="String"> One fell out and bumped his head.<span class="String">&quot;</span></span>
</pre><pre class="idle"><span class="Storage">var</span> __a, lyrics, num;
<span class="Keyword">if</span> (<span class="Variable">this</span>.studying_economics) {
<span class="Keyword">while</span> (supply <span class="Keyword">&gt;</span> demand) {
buy();
}
<span class="Keyword">while</span> (supply <span class="Keyword">&lt;</span> demand) {
sell();
}
}
<span class="Keyword">while</span> (supply <span class="Keyword">&gt;</span> demand) {
buy();
num <span class="Keyword">=</span> <span class="Number">6</span>;
lyrics <span class="Keyword">=</span> (<span class="Storage">function</span>() {
__a <span class="Keyword">=</span> [];
<span class="Keyword">while</span> (num <span class="Keyword">-</span><span class="Keyword">=</span> <span class="Number">1</span>) {
__a.<span class="LibraryFunction">push</span>(num <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> little monkeys, jumping on the bed. \</span>
<span class="String">One fell out and bumped his head.<span class="String">&quot;</span></span>);
}
<span class="Keyword">return</span> __a;
})();
</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 +744,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">&lt;</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">&lt;</span> __b.<span class="LibraryConstant">length</span>; __c<span class="Keyword">++</span>) {
food <span class="Keyword">=</span> __b[__c];
__a.<span class="LibraryFunction">push</span>(eat(food));
}
<span class="Keyword">return</span> __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">&lt;</span>__d.<span class="LibraryConstant">length</span>; __e<span class="Keyword">++</span>) {
<span class="Keyword">for</span> (__e <span class="Keyword">=</span> <span class="Number">0</span>; __e <span class="Keyword">&lt;</span> __d.<span class="LibraryConstant">length</span>; __e<span class="Keyword">++</span>) {
roid <span class="Keyword">=</span> __d[__e];
__f <span class="Keyword">=</span> asteroids;
<span class="Keyword">for</span> (__g<span class="Keyword">=</span><span class="Number">0</span>; __g<span class="Keyword">&lt;</span>__f.<span class="LibraryConstant">length</span>; __g<span class="Keyword">++</span>) {
<span class="Keyword">for</span> (__g <span class="Keyword">=</span> <span class="Number">0</span>; __g <span class="Keyword">&lt;</span> __f.<span class="LibraryConstant">length</span>; __g<span class="Keyword">++</span>) {
roid2 <span class="Keyword">=</span> __f[__g];
<span class="Keyword">if</span> (roid <span class="Keyword">!</span><span class="Keyword">==</span> roid2) {
<span class="Keyword">if</span> (roid.overlaps(roid2)) {
@@ -743,82 +780,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">&lt;=</span> __e ? num <span class="Keyword">&lt;=</span> __e : num <span class="Keyword">&gt;=</span> __e); (__d <span class="Keyword">&lt;=</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">&lt;=</span> __j ? i <span class="Keyword">&lt;</span> __j : i <span class="Keyword">&gt;</span> __j); (__i <span class="Keyword">&lt;=</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">&quot;</span> is <span class="String">&quot;</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">&quot;</span> is <span class="String">&quot;</span></span> <span class="Keyword">+</span> age
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, age, ages, child, years_old;
<span class="Storage">var</span> __hasProp <span class="Keyword">=</span> <span class="LibraryClassType">Object</span>.<span class="LibraryConstant">prototype</span>.hasOwnProperty;
years_old <span class="Keyword">=</span> {
max: <span class="Number">10</span>,
ida: <span class="Number">9</span>,
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">&quot;</span> is <span class="String">&quot;</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">&quot;</span> is <span class="String">&quot;</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>
@@ -927,28 +967,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>
@@ -989,25 +1031,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">=&gt;</span>
<span class="FunctionName">Animal::move</span><span class="Keyword">:</span> <span class="FunctionArgument">meters</span> <span class="Storage">=&gt;</span>
alert(<span class="Variable">this</span>.name <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>)
<span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</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">=&gt;</span> <span class="FunctionName">this.name</span><span class="Keyword">:</span> name
Snake <span class="Variable">extends</span> Animal
<span class="FunctionName">Snake::move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(<span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>)
<span class="Variable">super</span>(<span class="Number">5</span>)
<span class="FunctionName">Horse</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</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">=&gt;</span> <span class="FunctionName">this.name</span><span class="Keyword">:</span> name
Horse <span class="Variable">extends</span> Animal
<span class="FunctionName">Horse::move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(<span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>)
@@ -1117,12 +1159,157 @@ 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">=&gt;</span>
<span class="Comment"><span class="Comment">#</span> Make an Ajax request to fetch the weather...</span>
[location, <span class="Number">72</span>, <span class="String"><span class="String">&quot;</span>Mostly Sunny<span class="String">&quot;</span></span>]
[city, temp, forecast]<span class="Keyword">:</span> weather_report(<span class="String"><span class="String">&quot;</span>Berkeley, CA<span class="String">&quot;</span></span>)
</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">&quot;</span>Mostly Sunny<span class="String">&quot;</span></span>];
};
__a <span class="Keyword">=</span> weather_report(<span class="String"><span class="String">&quot;</span>Berkeley, CA<span class="String">&quot;</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">&quot;</span>Umberto Boccioni<span class="String">&quot;</span></span>
<span class="FunctionName">painter</span><span class="Keyword">:</span> <span class="String"><span class="String">&quot;</span>Vladimir Burliuk<span class="String">&quot;</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">&quot;</span>F.T. Marinetti<span class="String">&quot;</span></span>
<span class="FunctionName">address</span><span class="Keyword">:</span> [
<span class="String"><span class="String">&quot;</span>Via Roma 42R<span class="String">&quot;</span></span>
<span class="String"><span class="String">&quot;</span>Bellagio, Italy 22021<span class="String">&quot;</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">&quot;</span>Umberto Boccioni<span class="String">&quot;</span></span>,
painter: <span class="String"><span class="String">&quot;</span>Vladimir Burliuk<span class="String">&quot;</span></span>,
poet: {
name: <span class="String"><span class="String">&quot;</span>F.T. Marinetti<span class="String">&quot;</span></span>,
address: [<span class="String"><span class="String">&quot;</span>Via Roma 42R<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Bellagio, Italy 22021<span class="String">&quot;</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">=&gt;</span>
<span class="FunctionName">this.customer</span><span class="Keyword">:</span> customer
<span class="FunctionName">this.cart</span><span class="Keyword">:</span> cart
$(<span class="String"><span class="String">'</span>.shopping_cart<span class="String">'</span></span>).bind(<span class="String"><span class="String">'</span>click<span class="String">'</span></span>) <span class="FunctionName">event</span> <span class="Keyword">=</span><span class="Storage">=&gt;</span>
<span class="Variable">this</span>.customer.purchase(<span class="Variable">this</span>.cart)
</pre><pre class="idle"><span class="Storage">var</span> Account;
Account <span class="Keyword">=</span> <span class="Storage">function</span> <span class="FunctionName">Account</span>(<span class="FunctionArgument">customer, cart</span>) {
<span class="Storage">var</span> __a, __b;
<span class="Storage">var</span> __this <span class="Keyword">=</span> <span class="Variable">this</span>;
<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>() {
<span class="FunctionName">__b</span> = <span class="Storage">function</span>(<span class="FunctionArgument">event</span>) {
<span class="Storage">var</span> __c;
__c <span class="Keyword">=</span> <span class="Variable">this</span>.customer.purchase(<span class="Variable">this</span>.cart);
<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> : __c;
};
<span class="Keyword">return</span> (<span class="Storage">function</span>() {
<span class="Keyword">return</span> __b.<span class="LibraryFunction">apply</span>(__this, arguments);
});
})());
<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, &quot;Hello JavaScript&quot;].join(&quot;: &quot;);</span>
@@ -1149,25 +1336,33 @@ return [document.title, "Hello JavaScript"].join(": ");
in a returnable, assignable expression. The format is: <tt>switch</tt> condition,
<tt>when</tt> clauses, <tt>else</tt> the default case.
</p>
<p>
As in Ruby, <b>switch</b> statements in CoffeeScript can take multiple
values for each <b>when</b> clause. If any of the values match, the clause
runs.
</p>
<div class='code'><pre class="idle"><span class="Keyword">switch</span> day
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Tuesday<span class="String">&quot;</span></span> <span class="Keyword">then</span> eat_breakfast()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Wednesday<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_the_park()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Saturday<span class="String">&quot;</span></span>
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Mon<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_work()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Tue<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_the_park()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Thu<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_ice_fishing()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Fri<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>Sat<span class="String">&quot;</span></span>
<span class="Keyword">if</span> day <span class="Keyword">is</span> bingo_day
go_to_bingo()
go_dancing()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Sunday<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_church()
<span class="Keyword">when</span> <span class="String"><span class="String">&quot;</span>Sun<span class="String">&quot;</span></span> <span class="Keyword">then</span> go_to_church()
<span class="Keyword">else</span> go_to_work()
</pre><pre class="idle"><span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Tuesday<span class="String">&quot;</span></span>) {
eat_breakfast();
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Wednesday<span class="String">&quot;</span></span>) {
</pre><pre class="idle"><span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Mon<span class="String">&quot;</span></span>) {
go_to_work();
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Tue<span class="String">&quot;</span></span>) {
go_to_the_park();
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Saturday<span class="String">&quot;</span></span>) {
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Thu<span class="String">&quot;</span></span>) {
go_ice_fishing();
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Fri<span class="String">&quot;</span></span> <span class="Keyword">||</span> day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Sat<span class="String">&quot;</span></span>) {
<span class="Keyword">if</span> (day <span class="Keyword">===</span> bingo_day) {
go_to_bingo();
go_dancing();
}
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Sunday<span class="String">&quot;</span></span>) {
} <span class="Keyword">else</span> <span class="Keyword">if</span> (day <span class="Keyword">===</span> <span class="String"><span class="String">&quot;</span>Sun<span class="String">&quot;</span></span>) {
go_to_church();
} <span class="Keyword">else</span> {
go_to_work();
@@ -1197,7 +1392,7 @@ return [document.title, "Hello JavaScript"].join(": ");
</pre><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">&quot;</span>Call me Ishmael. Some years ago --</span>
@@ -1223,6 +1418,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"> &lt;strong&gt;</span>
<span class="String"> cup of coffeescript</span>
<span class="String"> &lt;/strong&gt;</span>
<span class="String"> <span class="String">'</span></span><span class="String"><span class="String">'</span><span class="String">'</span></span>
</pre><pre class="idle"><span class="Storage">var</span> html;
html <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>&lt;strong&gt;<span class="UserDefinedConstant">\n</span> cup of coffeescript<span class="UserDefinedConstant">\n</span>&lt;/strong&gt;<span class="String">&quot;</span></span>;
</pre><br class='clear' /></div>
<h2 id="resources">Resources</h2>
@@ -1231,7 +1440,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.
@@ -1281,6 +1490,28 @@ world...";
<h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 20px;">0.2.5</b>
The conditions in switch statements can now take multiple values at once &mdash;
If any of them are true, the case will run. Added the long arrow <tt>==></tt>,
which defines and immediately binds a function to <tt>this</tt>. While loops can
now be used as expressions, in the same way that comprehensions can. Splats
can be used within pattern matches to soak up the rest of an array.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.2.4</b>
Added ECMAScript Harmony style destructuring assignment, for dealing with
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

View File

@@ -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.5' # Keep in sync with the gemspec.
# Compile a script (String or IO) to JavaScript.
def self.compile(script, options={})

View File

@@ -43,7 +43,7 @@
<key>comment</key>
<string>match stuff like: funcName: =&gt; … </string>
<key>match</key>
<string>([a-zA-Z0-9_?.$:*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</string>
<string>([a-zA-Z0-9_?.$:*]*?)\s*(=\b|:\b)\s*([\w,\s]*?)\s*(=+&gt;)</string>
<key>name</key>
<string>meta.function.coffee</string>
</dict>
@@ -64,7 +64,7 @@
<key>comment</key>
<string>match stuff like: a =&gt; … </string>
<key>match</key>
<string>([a-zA-Z0-9_?., $:*]*)\s*(=&gt;)</string>
<string>([a-zA-Z0-9_?., $*]*)\s*(=+&gt;)</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>
@@ -208,13 +232,13 @@
</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>
@@ -263,7 +287,7 @@
</dict>
<dict>
<key>match</key>
<string>!|%|&amp;|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\b(in|ino|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
<string>!|%|&amp;|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\b(instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
<key>name</key>
<string>keyword.operator.coffee</string>
</dict>

View File

@@ -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
@@ -34,12 +34,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 '||=' '&&='
right RETURN
right '=>' UNLESS IF ELSE WHILE
right '=>' '==>' UNLESS IF ELSE WHILE
preclow
rule
@@ -198,8 +198,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 +217,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.
@@ -276,7 +282,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.
@@ -315,6 +320,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 +353,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 +373,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 +396,8 @@ rule
# An individual when.
When:
LEADING_WHEN Expression Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
| LEADING_WHEN Expression Block
LEADING_WHEN SimpleArgs Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
| LEADING_WHEN SimpleArgs Block
Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
| Comment Terminator When { result = val[2].add_comment(val[0]) }
;

View File

@@ -12,7 +12,7 @@ module CoffeeScript
"new", "return",
"try", "catch", "finally", "throw",
"break", "continue",
"for", "in", "ino", "by", "where", "while",
"for", "in", "of", "by", "where", "while",
"switch", "when",
"super", "extends",
"arguments",
@@ -22,21 +22,24 @@ module CoffeeScript
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([+\*&|\/\-%=<>:!]+)/
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 +52,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 +72,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 +91,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 +107,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 +156,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

View File

@@ -20,17 +20,13 @@
// 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, 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;
}

View File

@@ -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 = ' '
@@ -30,9 +36,9 @@ 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]
closure ? compile_closure(@options) : compile_node(@options)
end
def compile_closure(o={})
@@ -41,7 +47,7 @@ module CoffeeScript
"(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
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
@@ -50,6 +56,7 @@ module CoffeeScript
def unwrap; self; 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.
@@ -57,7 +64,8 @@ module CoffeeScript
statement
attr_reader :expressions
STRIP_TRAILING_WHITESPACE = /\s+$/
TRAILING_WHITESPACE = /\s+$/
UPPERCASE = /[A-Z]/
# Wrap up a node as an Expressions, unless it already is.
def self.wrap(*nodes)
@@ -69,12 +77,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
@@ -91,36 +100,19 @@ module CoffeeScript
node == @expressions[@last_index]
end
# Determine if this is the expressions body within a constructor function.
# Constructors are capitalized by CoffeeScript convention.
def constructor?(o)
o[:top] && o[:last_assign] && o[:last_assign][0..0][UPPERCASE]
end
def compile(o={})
o[:scope] ? super(o) : compile_root(o)
end
# 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.
@@ -129,15 +121,34 @@ module CoffeeScript
@indent = indent
o.merge!(:indent => indent, :scope => Scope.new(nil, self))
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
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 constructor?(o)
# It's the last line of a constructor, add a safety check.
temp = o[:scope].free_variable
"#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:last_assign]} === this.constructor ? this : #{temp};"
end
end
@@ -145,14 +156,18 @@ module CoffeeScript
# Literals are static values that have a Ruby representation, eg.: a string, a number,
# true, false, nil, etc.
class LiteralNode < Node
# 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)'
}
# If we get handed a literal reference to an arguments object, convert
# it to an array.
ARG_ARRAY = 'Array.prototype.slice.call(arguments, 0)'
attr_reader :value
# Wrap up a compiler-generated string as a LiteralNode.
def self.wrap(string)
self.new(Value.new(string))
end
@@ -167,14 +182,14 @@ module CoffeeScript
alias_method :statement_only?, :statement?
def compile_node(o)
val = CONVERSIONS[@value.to_s] || @value.to_s
@value = ARG_ARRAY if @value.to_s.to_sym == :arguments
indent = statement? ? idt : ''
ending = statement? ? ';' : ''
write("#{indent}#{val}#{ending}")
write "#{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
@@ -202,8 +217,7 @@ module CoffeeScript
def compile_node(o={})
delimiter = "\n#{idt}//"
comment = "#{delimiter}#{@lines.join(delimiter)}"
write(comment)
write("#{delimiter}#{@lines.join(delimiter)}")
end
end
@@ -231,13 +245,14 @@ module CoffeeScript
end
def splat?
@arguments.any? {|a| a.is_a?(ArgSplatNode) }
@arguments.any? {|a| a.is_a?(SplatNode) }
end
def <<(argument)
@arguments << argument
end
# Compile a vanilla function call.
def compile_node(o)
return write(compile_splat(o)) if splat?
args = @arguments.map{|a| a.compile(o) }.join(', ')
@@ -245,6 +260,7 @@ module CoffeeScript
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]
arg_part = args.empty? ? '' : ", #{args}"
@@ -253,12 +269,13 @@ module CoffeeScript
"#{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('')})"
@@ -275,6 +292,7 @@ module CoffeeScript
@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 +307,10 @@ module CoffeeScript
# A value, indexed or dotted into, or vanilla.
class ValueNode < Node
attr_reader :literal, :properties, :last, :source
attr_reader :base, :properties, :last, :source
def initialize(literal, properties=[])
@literal, @properties = literal, properties
def initialize(base, properties=[])
@base, @properties = base, properties
end
def <<(other)
@@ -304,23 +322,35 @@ 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
# 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
@@ -360,14 +390,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 +398,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
@@ -426,18 +449,49 @@ module CoffeeScript
end
def compile_node(o)
return compile_splice(o) if @variable.properties.last.is_a?(SliceNode)
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 = @variable.last.to_s.sub(LEADING_DOT, '')
last = value? ? @variable.last.to_s.sub(LEADING_DOT, '') : name
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)
return write("#{name}: #{@value.compile(o)}") if @context == :object
o[:scope].find(name) unless @variable.properties?
o[:scope].find(name) unless value? && @variable.properties?
val = "#{name} = #{@value.compile(o)}"
return write("#{idt}#{val};") if stmt
write(o[:return] ? "#{idt}return (#{val})" : val)
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(Value.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)
var = @variable.compile(o.merge(:only_first => true))
range = @variable.properties.last.range
@@ -496,14 +550,23 @@ module CoffeeScript
# A function definition. The only node that creates a new Scope.
class CodeNode < Node
attr_reader :params, :body
attr_reader :params, :body, :bound
def initialize(params, body)
def initialize(params, body, tag=nil)
@params = params
@body = body
@body = body
@bound = tag == :boundfunc
end
def statement?
@bound
end
def compile_node(o)
if @bound
o[:scope].assign("__this", "this")
fvar = o[:scope].free_variable
end
shared_scope = o.delete(:shared_scope)
o[:scope] = shared_scope || Scope.new(o[:scope], @body)
o[:return] = true
@@ -512,20 +575,23 @@ module CoffeeScript
o.delete(:no_wrap)
o.delete(:globals)
name = o.delete(:immediate_assign)
if @params.last.is_a?(ParamSplatNode)
if @params.last.is_a?(SplatNode)
splat = @params.pop
splat.index = @params.length
@body.unshift(splat)
end
@params.each {|id| o[:scope].parameter(id.to_s) }
code = @body.compile_with_declarations(o)
code = "\n#{@body.compile_with_declarations(o)}\n"
name_part = name ? " #{name}" : ''
write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{idt}}")
func = "function#{@bound ? '' : name_part}(#{@params.join(', ')}) {#{code}#{idt}}"
return write(func) unless @bound
write("#{idt}#{fvar} = #{func};\n#{idt}#{o[:return] ? 'return ' : ''}(function#{name_part}() {\n#{idt(1)}return #{fvar}.apply(__this, arguments);\n#{idt}});")
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
attr_accessor :index
attr_reader :name
@@ -534,20 +600,20 @@ module CoffeeScript
end
def compile_node(o={})
write(@index ? compile_param(o) : compile_arg(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_arg(o)
@name.compile(o)
end
def compile_value(o, name, index)
"Array.prototype.slice.call(#{name}, #{index})"
end
end
@@ -555,6 +621,7 @@ module CoffeeScript
# An object literal.
class ObjectNode < Node
attr_reader :properties
alias_method :objects, :properties
def initialize(properties = [])
@properties = properties
@@ -598,6 +665,17 @@ 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)
Expressions.wrap(CallNode.new(
ValueNode.new(LiteralNode.new(array), [AccessorNode.new('push')]),
[expressions.unwrap]
))
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
@@ -609,14 +687,25 @@ module CoffeeScript
@condition, @body = condition, body
end
def top_sensitive?
true
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
@@ -638,17 +727,21 @@ module CoffeeScript
@name, @index = @index, @name if @object
end
def top_sensitive?
true
end
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 +750,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 +759,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,8 +769,9 @@ 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)]),
CallNode.new(ValueNode.new(LiteralNode.wrap("__hasProp"), [AccessorNode.new(Value.new('call'))]), [LiteralNode.wrap(svar), LiteralNode.wrap(ivar)]),
Expressions.wrap(body),
nil,
{:statement => true}
@@ -773,6 +864,7 @@ module CoffeeScript
@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 +886,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 +909,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 +928,7 @@ 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}}"
if_part = "#{prefix}#{if_dent}if (#{compile_condition(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{idt}}"
return if_part unless @else_body
else_part = chain? ?
" else #{@else_body.compile(o.merge(:indent => idt, :chain_child => true))}" :

View File

@@ -5,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

View File

@@ -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

View File

@@ -47,15 +47,40 @@ module CoffeeScript
@temp_variable.dup
end
# Ensure that an assignment is made at the top of scope (or top-level
# scope, if requested).
def assign(name, value, top=false)
return @parent.assign(name, value, top) if top && @parent
@variables[name.to_sym] = Value.new(value)
end
def declarations?(body)
!declared_variables.empty? && body == @expressions
end
def assignments?(body)
!assigned_variables.empty? && body == @expressions
end
# Return the list of variables first declared in current scope.
def declared_variables
@variables.select {|k, v| v == :var }.map {|pair| pair[0].to_s }.sort
end
# Return the list of variables that are supposed to be assigned at the top
# of scope.
def assigned_variables
@variables.select {|k, v| v.is_a?(Value) }.sort_by {|pair| pair[0].to_s }
end
def compiled_declarations
declared_variables.join(', ')
end
def compiled_assignments
assigned_variables.map {|name, val| "#{name} = #{val}"}.join(', ')
end
def inspect
"<Scope:#{__id__} #{@variables.inspect}>"
end

View File

@@ -18,6 +18,10 @@ module CoffeeScript
to_str.to_sym
end
def compile(o={})
to_s
end
def inspect
@value.inspect
end
@@ -37,6 +41,10 @@ module CoffeeScript
def hash
@value.hash
end
def match(regex)
@value.match(regex)
end
end
end

View File

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

View File

@@ -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!")

View 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")

View File

@@ -26,4 +26,4 @@ func: =>
c.single: c.list[1..1][0]
print(func() == '-')
print(func() is '-')

View File

@@ -0,0 +1,22 @@
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')
obj: {
name: "Fred"
bound: =>
(==> print(this.name is "Fred"))()
unbound: =>
(=> print(!this.name?))()
}
obj.unbound()
obj.bound()

View 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")

View File

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

View File

@@ -1,3 +1,5 @@
array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
array[5..10]: [0, 0, 0]

View File

@@ -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))

View 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")

View File

@@ -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

View File

@@ -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