Compare commits

...

17 Commits
0.2.2 ... 0.2.4

Author SHA1 Message Date
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
34 changed files with 845 additions and 229 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.4' # Keep version in sync with coffee-script.rb
s.date = '2010-1-12'
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,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

@@ -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.4</a>
</p>
<h2>Table of Contents</h2>
@@ -75,10 +75,11 @@
<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="#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 +96,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.
@@ -415,8 +416,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 +482,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 +500,39 @@ 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="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()') %>
@@ -527,10 +556,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 +575,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.
@@ -588,6 +624,19 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</ul>
<h2 id="change_log">Change Log</h2>
<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>

View File

@@ -2,12 +2,12 @@
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;

View File

@@ -2,12 +2,12 @@
var __a, __b, globals, name;
// 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 (__b.hasOwnProperty(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,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

@@ -6,13 +6,13 @@
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 (__b.hasOwnProperty(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

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

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.4</a>
</p>
<h2>Table of Contents</h2>
@@ -61,10 +61,11 @@
<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="#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 +138,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 +179,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 +193,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.
@@ -707,12 +708,12 @@ 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;
@@ -743,51 +744,52 @@ __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;
years_old <span class="Keyword">=</span> {
max: <span class="Number">10</span>,
@@ -795,14 +797,14 @@ years_old <span class="Keyword">=</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> (__b.hasOwnProperty(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;
years_old = {
@@ -811,14 +813,14 @@ years_old = {
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 (__b.hasOwnProperty(child)) {
__a.push(child + " is " + age);
}
}
return __b;
return __a;
})();
;alert(ages.join(", "));'>run: ages.join(", ")</button><br class='clear' /></div>
@@ -927,28 +929,28 @@ 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="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> (__b.hasOwnProperty(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;
// 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 (__b.hasOwnProperty(name)) {
__a.push(name);
}
}
return __b;
return __a;
})()).slice(0, 10);
;alert(globals);'>run: globals</button><br class='clear' /></div>
<p>
@@ -989,25 +991,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>)
@@ -1118,11 +1120,121 @@ tom.move();
});
});
</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="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>
@@ -1197,7 +1309,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 +1335,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 +1357,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.
@@ -1280,6 +1406,19 @@ world...";
</ul>
<h2 id="change_log">Change Log</h2>
<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>

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

View File

@@ -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,7 +34,7 @@ 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 '||=' '&&='
@@ -361,7 +361,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

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,6 +22,7 @@ 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?(\s*)(.*?)\n?(\s*)"{3}|'{3}\n?(\s*)(.*?)\n?(\s*)'{3})/m
JS = /\A(``|`(.*?)([^\\]|\\\\)`)/m
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
WHITESPACE = /\A([ \t]+)/
@@ -69,6 +70,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 +89,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 +105,24 @@ 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(MULTILINER, " \\\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)
indent = match[2] || match[5]
doc = match[3] || match[6]
doc.gsub!(/\n#{indent}/, "\\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]

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 = ' '
@@ -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
@@ -57,7 +63,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 +76,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 +99,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 +120,33 @@ 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
return code unless o[:scope].declarations?(self)
write("#{idt}var #{o[:scope].declared_variables.join(', ')};\n#{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 +154,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 +180,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 +215,7 @@ module CoffeeScript
def compile_node(o={})
delimiter = "\n#{idt}//"
comment = "#{delimiter}#{@lines.join(delimiter)}"
write(comment)
write("#{delimiter}#{@lines.join(delimiter)}")
end
end
@@ -238,6 +250,7 @@ module CoffeeScript
@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 +258,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,6 +267,7 @@ 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'
@@ -275,6 +290,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 +305,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 +320,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 +388,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 +396,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,7 +447,9 @@ module CoffeeScript
end
def compile_node(o)
return compile_splice(o) if @variable.properties.last.is_a?(SliceNode)
return compile_pattern_match(o) if @variable.array? || @variable.object?
return compile_splice(o) if @variable.splice?
stmt = o.delete(:as_statement)
name = @variable.compile(o)
last = @variable.last.to_s.sub(LEADING_DOT, '')
proto = name[PROTO_ASSIGN, 1]
@@ -435,9 +458,31 @@ module CoffeeScript
return write("#{name}: #{@value.compile(o)}") if @context == :object
o[:scope].find(name) unless @variable.properties?
val = "#{name} = #{@value.compile(o)}"
return write("#{idt}#{val};") if stmt
write(o[:return] ? "#{idt}return (#{val})" : val)
end
def statement?
@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
assigns << AssignNode.new(
obj, ValueNode.new(Value.new(val_var), [access_class.new(Value.new(i.to_s))])
).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
@@ -555,6 +600,7 @@ module CoffeeScript
# An object literal.
class ObjectNode < Node
attr_reader :properties
alias_method :objects, :properties
def initialize(properties = [])
@properties = properties
@@ -640,15 +686,15 @@ module CoffeeScript
def compile_node(o)
top_level = o.delete(:top) && !o[:return]
range = @source.is_a?(ValueNode) && @source.literal.is_a?(RangeNode) && @source.properties.empty?
source = range ? @source.literal : @source
range = @source.is_a?(ValueNode) && @source.base.is_a?(RangeNode) && @source.properties.empty?
source = range ? @source.base : @source
scope = o[:scope]
name_found = @name && scope.find(@name)
index_found = @index && scope.find(@index)
body_dent = idt(1)
rvar = scope.free_variable unless top_level
svar = scope.free_variable
ivar = range ? name : @index ? @index : scope.free_variable
rvar = scope.free_variable unless top_level
if range
index_var = scope.free_variable
source_part = source.compile_variables(o)
@@ -657,8 +703,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

View File

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

View File

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

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

View File

@@ -0,0 +1,28 @@
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")

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