Compare commits

...

22 Commits
0.1.4 ... 0.1.6

Author SHA1 Message Date
Jeremy Ashkenas
8fe6fa1cd7 CoffeeScript 0.1.6 -- bugfixes 2009-12-27 12:49:11 -08:00
Jeremy Ashkenas
835db4b279 fixing paths for running
coffee compiles CoffeeScript source files into JavaScript.

Usage:
  coffee path/to/script.coffee
    -i, --interactive                run a CoffeeScript REPL (requires Narwhal)
    -r, --run                        compile and run a script (requires Narwhal)
    -o, --output [DIR]               set the directory for compiled JavaScript
    -w, --watch                      watch scripts for changes, and recompile
    -p, --print                      print the compiled JavaScript to stdout
    -l, --lint                       pipe the compiled JavaScript through JSLint
    -e, --eval                       compile a cli scriptlet or read from stdin
    -t, --tokens                     print the tokens that the lexer produces
    -v, --verbose                    print at every step of code generation
    -n, --no-wrap                    raw output, no safety wrapper or vars
        --install-bundle             install the CoffeeScript TextMate bundle
        --version                    display CoffeeScript version
    -h, --help                       display this help message outside of the coffee-script directory
2009-12-27 12:43:05 -08:00
Jeremy Ashkenas
f89c864911 more underscore examples 2009-12-27 11:01:19 -08:00
Jeremy Ashkenas
542726911a more underscore and bugfix edits to code generation 2009-12-26 22:24:21 -08:00
Jeremy Ashkenas
575dc7d12e more underscore, and removing custom_assign and return from conditional compilation 2009-12-26 21:55:56 -08:00
Jeremy Ashkenas
ff0062b088 coffeescript 0.1.5, just for kicks 2009-12-26 21:25:37 -08:00
Jeremy Ashkenas
78589f5db1 docs for range comprehensiosn 2009-12-26 20:46:31 -08:00
Jeremy Ashkenas
903331f3ff got negative ranges working with (much, much) uglier compiled code 2009-12-26 20:35:43 -08:00
Jeremy Ashkenas
47b45c4494 removing no_paren -- it was optimizing away order of operations 2009-12-26 11:55:34 -08:00
Jeremy Ashkenas
c4844abb28 adding newline escaping, with tests 2009-12-26 09:29:03 -08:00
Jeremy Ashkenas
96ae6d80f3 docs 2009-12-26 09:05:57 -08:00
Jeremy Ashkenas
60342e8cd9 changed bin/coffee-script to bin/coffee 2009-12-26 08:57:13 -08:00
Jeremy Ashkenas
f9c3d3fc14 fixed range comprehension indexing 2009-12-26 00:27:49 -08:00
Jeremy Ashkenas
b1e25eea88 trading the cs> prompt for the coffee> prompt 2009-12-26 00:18:24 -08:00
Jeremy Ashkenas
1486bbab9f added array comprehensions over ranges 2009-12-26 00:16:40 -08:00
Jeremy Ashkenas
59d912cc26 docs for assignment-as-expression 2009-12-25 23:17:34 -08:00
Jeremy Ashkenas
9adf2e2d30 major internal reworking -- all variable declarations have been pushed up to the first line of the block scope -- all assignment is now an inherent expression 2009-12-25 22:57:33 -08:00
Jeremy Ashkenas
cf46fa8c2c started raising syntax errors for parens wrapped around expressions (they used to silently be ignored) 2009-12-25 20:36:22 -08:00
Jeremy Ashkenas
16ca3d1608 don't add the no_wrap key to the options hash unless we're going to use it 2009-12-25 19:48:47 -08:00
Jeremy Ashkenas
476a251c80 comment 2009-12-25 19:34:40 -08:00
Jeremy Ashkenas
274152aff7 documenting ranges and slices 2009-12-25 16:35:57 -08:00
Jeremy Ashkenas
00659e5f76 reorganizing test fixtures and adding range literals for array slices 2009-12-25 16:20:28 -08:00
64 changed files with 1699 additions and 1466 deletions

2
README
View File

@@ -26,7 +26,7 @@
gem install coffee-script
Compile a script:
coffee-script /path/to/script.coffee
coffee /path/to/script.coffee
For documentation, usage, and examples, see:
http://jashkenas.github.com/coffee-script/

View File

@@ -19,7 +19,7 @@ namespace :build do
desc "Compile the Narwhal interface for --interactive and --run"
task :narwhal do
sh "bin/coffee-script lib/coffee_script/narwhal/*.coffee -o lib/coffee_script/narwhal/js"
sh "bin/coffee lib/coffee_script/narwhal/*.coffee -o lib/coffee_script/narwhal/js"
end
end
@@ -27,7 +27,7 @@ end
desc "Build the documentation page"
task :doc do
source = 'documentation/index.html.erb'
child = fork { exec "bin/coffee-script documentation/coffee/*.coffee -o documentation/js -w" }
child = fork { exec "bin/coffee documentation/coffee/*.coffee -o documentation/js -w" }
at_exit { Process.kill("INT", child) }
Signal.trap("INT") { exit }
loop do

View File

@@ -1,7 +1,7 @@
Gem::Specification.new do |s|
s.name = 'coffee-script'
s.version = '0.1.4' # Keep version in sync with coffee-script.rb
s.date = '2009-12-25'
s.version = '0.1.6' # Keep version in sync with coffee-script.rb
s.date = '2009-12-27'
s.homepage = "http://jashkenas.github.com/coffee-script/"
s.summary = "The CoffeeScript Compiler"
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
s.has_rdoc = false
s.require_paths = ['lib']
s.executables = ['coffee-script']
s.executables = ['coffee']
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README']
end

View File

@@ -2,4 +2,4 @@
lunch: food.eat() for food in ['toast', 'cheese', 'wine'].
# Zebra-stripe a table.
highlight(row) for row, i in table if i % 2 is 0.
highlight(row) for row, i in table if i % 2 is 0.

View File

@@ -2,3 +2,4 @@ hi: `function() {
return [document.title, "Hello JavaScript"].join(": ");
}`

View File

@@ -0,0 +1 @@
six: (one: 1) + (two: 2) + (three: 3)

View File

@@ -1,2 +1,6 @@
nums: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
three_to_six: nums[3, 6]
numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
three_to_six: numbers[3..6]
numbers_copy: numbers[0...numbers.length]

View File

@@ -5,3 +5,4 @@ to interest me on shore, I thought I would sail
about a little and see the watery part of the
world..."

View File

@@ -48,6 +48,8 @@ code, pre, tt {
font-size: 12px;
line-height: 18px;
color: #191955;
white-space: pre-wrap;
word-wrap: break-word;
}
tt {
background: #f8f8ff;

View File

@@ -64,16 +64,17 @@
<a href="#aliases">Aliases</a><br />
<a href="#while">While Loops</a><br />
<a href="#array_comprehensions">Array Comprehensions</a><br />
<a href="#slice">Array Slice Literals</a><br />
<a href="#slice">Slicing Arrays with Ranges</a><br />
<a href="#inheritance">Inheritance, and Calling Super from a Subclass</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="#resources">Resources</a><br />
<a href="#contributing">Contributing</a><br />
<a href="#change_log">Change Log</a><br />
</p>
<h2 id="overview">Mini Overview</h2>
<p><i>CoffeeScript on the left, compiled JavaScript output on the right.</i></p>
@@ -91,12 +92,12 @@
gem install coffee-script</pre>
<p>
Installing the gem provides the <tt>coffee-script</tt> command, which can
Installing the gem provides the <tt>coffee</tt> command, which can
be used to compile CoffeeScript <tt>.coffee</tt> files into JavaScript, as
well as debug them. In conjunction with
<a href="http://narwhaljs.org/">Narwhal</a>, the <tt>coffee-script</tt>
<a href="http://narwhaljs.org/">Narwhal</a>, the <tt>coffee</tt>
command also provides direct evaluation and an interactive REPL.
When compiling to JavaScript, <tt>coffee-script</tt> writes the output
When compiling to JavaScript, <tt>coffee</tt> writes the output
as <tt>.js</tt> files in the same directory by default, but output
can be customized with the following options:
</p>
@@ -148,7 +149,7 @@ gem install coffee-script</pre>
<td><code>-e, --eval</code></td>
<td>
Compile and print a little snippet of CoffeeScript directly from the
command line (or from <b>stdin</b>). For example:<br /><tt>coffee-script -e "square: x => x * x."</tt>
command line (or from <b>stdin</b>). For example:<br /><tt>coffee -e "square: x => x * x."</tt>
</td>
</tr>
<tr>
@@ -187,10 +188,10 @@ gem install coffee-script</pre>
</p>
<pre>
coffee-script path/to/script.coffee
coffee-script --interactive
coffee-script --watch --lint experimental.coffee
coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
coffee path/to/script.coffee
coffee --interactive
coffee --watch --lint experimental.coffee
coffee --print app/scripts/*.coffee > concatenation.js</pre>
<h2>Language Reference</h2>
@@ -230,6 +231,10 @@ coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
mathy things.
</p>
<%= code_for('assignment', 'greeting') %>
<p>
Declarations of new variables are pushed up to the top of the current scope,
so that assignments may always be used within expressions.
</p>
<p id="objects_and_arrays">
<b class="header">Objects and Arrays</b>
@@ -293,7 +298,12 @@ coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
<p>
The same mechanism is used to push down assignment through <b>switch</b>
statements, and <b>if-elses</b> (although the ternary operator is preferred).
Another part of manipulating assignment statements is
CoffeeScript's declaration of new variables at the top of the
current scope. This allows assignment to be used as a piece of an
expression.
</p>
<%= code_for('expressions_assignment', 'six') %>
<p id="aliases">
<b class="header">Aliases</b>
@@ -347,14 +357,22 @@ coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
would use a loop, <b>each</b>/<b>forEach</b>, <b>map</b>, or <b>select</b>/<b>filter</b>.
</p>
<%= code_for('array_comprehensions') %>
<p>
If you're not iterating over an actual array, you can use a range to
specify the start and end of an array comprehension:
<tt>coundown(i) for i in [10..1].</tt>
</p>
<p id="slice">
<b class="header">Array Slice Literals</b>
CoffeeScript includes syntax for extracting slices of arrays.
The first argument is the index of the first element in the slice, and
the second is the index of the last one.
<b class="header">Slicing Arrays with Ranges</b>
CoffeeScript borrows Ruby's
<a href="http://ruby-doc.org/core/classes/Range.html">range syntax</a>
for extracting slices of arrays. With two dots (<tt>3..5</tt>), the range
is inclusive: the first argument is the index of the first element in
the slice, and the second is the index of the last one. Three dots signify
a range that excludes the end.
</p>
<%= code_for('slices', 'three_to_six') %>
<%= code_for('slices', 'numbers_copy') %>
<p id="inheritance">
<b class="header">Inheritance, and Calling Super from a Subclass</b>
@@ -406,6 +424,13 @@ coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
Multiline strings are allowed in CoffeeScript.
</p>
<%= code_for('strings', 'moby_dick') %>
<h2 id="resources">Resources</h2>
<p>
<a href="http://github.com/jashkenas/coffee-script/">Source Code</a><br />
<a href="http://github.com/jashkenas/coffee-script/issues">Bugs and Feature Requests</a><br />
</p>
<h2 id="contributing">Contributing</h2>
@@ -421,7 +446,8 @@ coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
</li>
<li>
Ideas for alternate syntax to end blocks of expressions &mdash; the periods
can look a little weird with deeply nested structure.
can look a little weird with deeply nested structure. (There's now a
'whitespace' branch &mdash; help add significant whitespace over there.)
</li>
<li>
Test cases for any syntax errors you encounter that you think CoffeeScript
@@ -444,21 +470,37 @@ coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
<h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 20px;">0.1.6</b>
Bugfix for running <tt>coffee --interactive</tt> and <tt>--run</tt>
from outside of the CoffeeScript directory. Bugfix for nested
function/if-statements.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.5</b>
Array slice literals and array comprehensions can now both take Ruby-style
ranges to specify the start and end. JavaScript variable declaration is
now pushed up to the top of the scope, making all assignment statements into
expressions. You can use <tt>\</tt> to escape newlines.
The <tt>coffee-script</tt> command is now called <tt>coffee</tt>.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.4</b>
The official CoffeeScript extension is now <tt>.coffee</tt> instead of
<tt>.cs</tt>, which properly belongs to
<tt>.cs</tt>, which properly belongs to
<a href="http://en.wikipedia.org/wiki/C_Sharp_(programming_language)">C#</a>.
Due to popular demand, you can now also use <tt>=</tt> to assign. Unlike
JavaScript, <tt>=</tt> can also be used within object literals, interchangeably
with <tt>:</tt>. Made a grammatical fix for chained function calls
like <tt>func(1)(2)(3)(4)</tt>. Inheritance and super no longer use
<tt>__proto__</tt>, so they should be IE-compatible now.
with <tt>:</tt>. Made a grammatical fix for chained function calls
like <tt>func(1)(2)(3)(4)</tt>. Inheritance and super no longer use
<tt>__proto__</tt>, so they should be IE-compatible now.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.3</b>
The <tt>coffee-script</tt> command now includes <tt>--interactive</tt>,
The <tt>coffee</tt> command now includes <tt>--interactive</tt>,
which launches an interactive CoffeeScript session, and <tt>--run</tt>,
which directly compiles and executes a script. Both options depend on a
working installation of Narwhal.

View File

@@ -1,8 +1,8 @@
(function(){
var volume;
if (ignition === true) {
launch();
}
var volume;
if (band !== spinal_tap) {
volume = 10;
}

View File

@@ -1,20 +1,19 @@
(function(){
var __a, __b, __c, __d, __e, __f, __g, __h, food, i, lunch, row;
// Eat lunch.
var lunch;
var __a = ['toast', 'cheese', 'wine'];
var __d = [];
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var food = __a[__b];
__a = ['toast', 'cheese', 'wine'];
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
food = __a[__b];
__d[__b] = food.eat();
}
lunch = __d;
// Zebra-stripe a table.
var __e = table;
var __h = [];
for (var __f=0, __g=__e.length; __f<__g; __f++) {
var row = __e[__f];
var i = __f;
__e = table;
__h = [];
for (__f=0, __g=__e.length; __f<__g; __f++) {
row = __e[__f];
i = __f;
__h[__f] = i % 2 === 0 ? highlight(row) : null;
}
__h;

View File

@@ -1,4 +1,5 @@
(function(){
var greeting = "Hello CoffeeScript";
var difficulty = 0.5;
var difficulty, greeting;
greeting = "Hello CoffeeScript";
difficulty = 0.5;
})();

View File

@@ -1,5 +1,5 @@
(function(){
var mood;
var date, mood;
if (singing) {
mood = greatly_improved;
}
@@ -7,6 +7,6 @@
claps_hands();
cha_cha_cha();
}
var date = friday ? sue : jill;
date = friday ? sue : jill;
expensive = expensive || do_the_math();
})();

View File

@@ -1,5 +1,6 @@
(function(){
var hi = function() {
var hi;
hi = function() {
return [document.title, "Hello JavaScript"].join(": ");
};
})();

View File

@@ -1,5 +1,6 @@
(function(){
var grade = function(student) {
var eldest, grade;
grade = function(student) {
if (student.excellent_work) {
return "A+";
} else if (student.okay_stuff) {
@@ -8,5 +9,5 @@
return "C";
}
};
var eldest = 24 > 21 ? "Liz" : "Ike";
eldest = 24 > 21 ? "Liz" : "Ike";
})();

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
(function(){
var song = ["do", "re", "mi", "fa", "so"];
var ages = {
var ages, song;
song = ["do", "re", "mi", "fa", "so"];
ages = {
max: 10,
ida: 9,
tim: 11

View File

@@ -1,20 +1,20 @@
(function(){
var __a, __b, __c, __d, cubed_list, list, math, num, number, opposite_day, square;
// Assignment:
var number = 42;
var opposite_day = true;
number = 42;
opposite_day = true;
// Conditions:
if (opposite_day) {
number = -42;
}
// Functions:
var square = function(x) {
square = function(x) {
return x * x;
};
// Arrays:
var list = [1, 2, 3, 4, 5];
list = [1, 2, 3, 4, 5];
// Objects:
var math = {
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
@@ -22,11 +22,10 @@
}
};
// Array comprehensions:
var cubed_list;
var __a = list;
var __d = [];
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var num = __a[__b];
__a = list;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
num = __a[__b];
__d[__b] = math.cube(num);
}
cubed_list = __d;

View File

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

View File

@@ -1,4 +1,6 @@
(function(){
var nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var three_to_six = nums.slice(3, 6 + 1);
var numbers, numbers_copy, three_to_six;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
three_to_six = numbers.slice(3, 6 + 1);
numbers_copy = numbers.slice(0, numbers.length);
})();

View File

@@ -1,5 +1,6 @@
(function(){
var moby_dick = "Call me Ishmael. Some years ago -- \
var moby_dick;
moby_dick = "Call me Ishmael. Some years ago -- \
never mind how long precisely -- having little \
or no money in my purse, and nothing particular \
to interest me on shore, I thought I would sail \

View File

@@ -1,12 +1,12 @@
(function(){
var Animal = function() {
var Animal, Horse, Snake, sam, tom;
Animal = function() {
};
Animal.prototype.move = function(meters) {
return alert(this.name + " moved " + meters + "m.");
};
var Snake = function(name) {
this.name = name;
return this.name;
Snake = function(name) {
return (this.name = name);
};
Snake.__superClass__ = Animal.prototype;
Snake.prototype = new Animal();
@@ -15,9 +15,8 @@
alert("Slithering...");
return Snake.__superClass__.move.call(this, 5);
};
var Horse = function(name) {
this.name = name;
return this.name;
Horse = function(name) {
return (this.name = name);
};
Horse.__superClass__ = Animal.prototype;
Horse.prototype = new Animal();
@@ -26,8 +25,8 @@
alert("Galloping...");
return Horse.__superClass__.move.call(this, 45);
};
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");
sam = new Snake("Sammy the Python");
tom = new Horse("Tommy the Palomino");
sam.move();
tom.move();
})();

View File

@@ -132,7 +132,7 @@ wednesday: => eat_breakfast(); go_to_work(); eat_dinner(); .
# Array slice literals.
zero_to_nine: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
three_to_six: zero_to_nine[3, 6]
three_to_six: zero_to_nine[3..6]
# Multiline strings with inner quotes.
story: "Lorem ipsum dolor \"sit\" amet, consectetuer adipiscing elit,

View File

@@ -149,5 +149,5 @@ wipe_mutterings_from: sentence =>
while sentence.indexOf('(') >= 0
open: sentence.indexOf('(') - 1
close: sentence.indexOf(')') + 1
sentence: sentence[0, open] + sentence[close, sentence.length].
sentence: sentence[0..open] + sentence[close..sentence.length].
sentence.

View File

@@ -93,77 +93,63 @@ _.reduceRight: obj, memo, iterator, context =>
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context).
results: []
_.each(obj, (value, index, list =>
iterator.call(context, value, index, list) and results.push(value).))
results.push(value) if iterator.call(context, value, index, list).))
results.
#
# # Return all the elements for which a truth test fails.
# _.reject = function(obj, iterator, context) {
# var results = [];
# _.each(obj, function(value, index, list) {
# !iterator.call(context, value, index, list) && results.push(value);
# });
# return results;
# };
#
# # Determine whether all of the elements match a truth test. Delegate to
# # JavaScript 1.6's every(), if it is present.
# _.all = function(obj, iterator, context) {
# iterator = iterator || _.identity;
# if (obj && _.isFunction(obj.every)) return obj.every(iterator, context);
# var result = true;
# _.each(obj, function(value, index, list) {
# if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
# });
# return result;
# };
#
# # Determine if at least one element in the object matches a truth test. Use
# # JavaScript 1.6's some(), if it exists.
# _.any = function(obj, iterator, context) {
# iterator = iterator || _.identity;
# if (obj && _.isFunction(obj.some)) return obj.some(iterator, context);
# var result = false;
# _.each(obj, function(value, index, list) {
# if (result = iterator.call(context, value, index, list)) _.breakLoop();
# });
# return result;
# };
#
# # Determine if a given value is included in the array or object,
# # based on '==='.
# _.include = function(obj, target) {
# if (_.isArray(obj)) return _.indexOf(obj, target) != -1;
# var found = false;
# _.each(obj, function(value) {
# if (found = value === target) _.breakLoop();
# });
# return found;
# };
#
# # Invoke a method with arguments on every item in a collection.
# _.invoke = function(obj, method) {
# var args = _.rest(arguments, 2);
# return _.map(obj, function(value) {
# return (method ? value[method] : value).apply(value, args);
# });
# };
#
# # Convenience version of a common use case of map: fetching a property.
# _.pluck = function(obj, key) {
# return _.map(obj, function(value){ return value[key]; });
# };
#
# # Return the maximum item or (item-based computation).
# _.max = function(obj, iterator, context) {
# if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
# var result = {computed : -Infinity};
# _.each(obj, function(value, index, list) {
# var computed = iterator ? iterator.call(context, value, index, list) : value;
# computed >= result.computed && (result = {value : value, computed : computed});
# });
# return result.value;
# };
# Return all the elements for which a truth test fails.
_.reject: obj, iterator, context =>
results: []
_.each(obj, (value, index, list =>
results.push(value) if not iterator.call(context, value, index, list).))
results.
# Determine whether all of the elements match a truth test. Delegate to
# JavaScript 1.6's every(), if it is present.
_.all: obj, iterator, context =>
iterator ||= _.identity
return obj.every(iterator, context) if obj and _.isFunction(obj.every)
result: true
_.each(obj, (value, index, list =>
_.breakLoop() unless result: result and iterator.call(context, value, index, list).))
result.
# Determine if at least one element in the object matches a truth test. Use
# JavaScript 1.6's some(), if it exists.
_.any: obj, iterator, context =>
iterator ||= _.identity
return obj.some(iterator, context) if obj and _.isFunction(obj.some)
result: false
_.each(obj, (value, index, list =>
_.breakLoop() if (result: iterator.call(context, value, index, list)).))
result.
# Determine if a given value is included in the array or object,
# based on '==='.
_.include: obj, target =>
return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
found: false
_.each(obj, (value =>
_.breakLoop() if (found: value is target).))
found.
# Invoke a method with arguments on every item in a collection.
_.invoke: obj, method =>
args: _.rest(arguments, 2)
_.map(obj, (value =>
(if method then value[method] else value.).apply(value, args).)).
# Convenience version of a common use case of map: fetching a property.
_.pluck: obj, key =>
_.map(obj, (value => value[key].)).
# Return the maximum item or (item-based computation).
_.max: obj, iterator, context =>
return Math.max.apply(Math, obj) if !iterator and _.isArray(obj)
result: {computed: -Infinity}
_.each(obj, (value, index, list =>
computed: if iterator then iterator.call(context, value, index, list) else value.
computed >= result.computed and (result: {value: value, computed: computed}).))
result.value.
#
# # Return the minimum element (or element-based computation).
# _.min = function(obj, iterator, context) {
@@ -320,18 +306,15 @@ _.reduceRight: obj, memo, iterator, context =>
# range[idx++] = i;
# }
# };
#
# /* ----------------------- Function Functions: -----------------------------*/
#
# # Create a function bound to a given object (assigning 'this', and arguments,
# # optionally). Binding with arguments is also known as 'curry'.
# _.bind = function(func, obj) {
# var args = _.rest(arguments, 2);
# return function() {
# return func.apply(obj || root, args.concat(_.toArray(arguments)));
# };
# };
#
# ----------------------- Function Functions: -----------------------------
# Create a function bound to a given object (assigning 'this', and arguments,
# optionally). Binding with arguments is also known as 'curry'.
_.bind: func, obj =>
args: _.rest(arguments, 2)
=> func.apply(obj or root, args.concat(_.toArray(arguments)))..
# # Bind all of an object's methods to that object. Useful for ensuring that
# # all callbacks defined on an object belong to it.
# _.bindAll = function(obj) {
@@ -347,36 +330,27 @@ _.reduceRight: obj, memo, iterator, context =>
# var args = _.rest(arguments, 2);
# return setTimeout(function(){ return func.apply(func, args); }, wait);
# };
#
# # Defers a function, scheduling it to run after the current call stack has
# # cleared.
# _.defer = function(func) {
# return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
# };
#
# # Returns the first function passed as an argument to the second,
# # allowing you to adjust arguments, run code before and after, and
# # conditionally execute the original function.
# _.wrap = function(func, wrapper) {
# return function() {
# var args = [func].concat(_.toArray(arguments));
# return wrapper.apply(wrapper, args);
# };
# };
#
# # Returns a function that is the composition of a list of functions, each
# # consuming the return value of the function that follows.
# _.compose = function() {
# var funcs = _.toArray(arguments);
# return function() {
# var args = _.toArray(arguments);
# for (var i=funcs.length-1; i >= 0; i--) {
# args = [funcs[i].apply(this, args)];
# }
# return args[0];
# };
# };
#
# Defers a function, scheduling it to run after the current call stack has
# cleared.
_.defer: func =>
_.delay.apply(_, [func, 1].concat(_.rest(arguments))).
# Returns the first function passed as an argument to the second,
# allowing you to adjust arguments, run code before and after, and
# conditionally execute the original function.
_.wrap: func, wrapper =>
=> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))..
# Returns a function that is the composition of a list of functions, each
# consuming the return value of the function that follows.
_.compose: =>
funcs: _.toArray(arguments)
=>
args: _.toArray(arguments)
args: [funcs[i]].apply(this, args) for i in [(funcs.length - 1)..0].
args[0]..
# /* ------------------------- Object Functions: ---------------------------- */
#
# # Retrieve the names of an object's properties.
@@ -408,81 +382,67 @@ _.reduceRight: obj, memo, iterator, context =>
# if (_.isArray(obj)) return obj.slice(0);
# return _.extend({}, obj);
# };
#
# # Perform a deep comparison to check if two objects are equal.
# _.isEqual = function(a, b) {
# # Check object identity.
# if (a === b) return true;
# # Different types?
# var atype = typeof(a), btype = typeof(b);
# if (atype != btype) return false;
# # Basic equality test (watch out for coercions).
# if (a == b) return true;
# # One is falsy and the other truthy.
# if ((!a && b) || (a && !b)) return false;
# # One of them implements an isEqual()?
# if (a.isEqual) return a.isEqual(b);
# # Check dates' integer values.
# if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
# # Both are NaN?
# if (_.isNaN(a) && _.isNaN(b)) return true;
# # Compare regular expressions.
# if (_.isRegExp(a) && _.isRegExp(b))
# return a.source === b.source &&
# a.global === b.global &&
# a.ignoreCase === b.ignoreCase &&
# a.multiline === b.multiline;
# # If a is not an object by this point, we can't handle it.
# if (atype !== 'object') return false;
# # Check for different array lengths before comparing contents.
# if (a.length && (a.length !== b.length)) return false;
# # Nothing else worked, deep compare the contents.
# var aKeys = _.keys(a), bKeys = _.keys(b);
# # Different object sizes?
# if (aKeys.length != bKeys.length) return false;
# # Recursive comparison of contents.
# for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
# return true;
# };
#
# # Is a given array or object empty?
# _.isEmpty = function(obj) {
# return _.keys(obj).length == 0;
# };
#
# # Is a given value a DOM element?
# _.isElement = function(obj) {
# return !!(obj && obj.nodeType == 1);
# };
#
# # Is a given variable an arguments object?
# _.isArguments = function(obj) {
# return obj && _.isNumber(obj.length) && !_.isArray(obj) && !propertyIsEnumerable.call(obj, 'length');
# };
#
# # Is the given value NaN -- this one is interesting. NaN != NaN, and
# # isNaN(undefined) == true, so we make sure it's a number first.
# _.isNaN = function(obj) {
# return _.isNumber(obj) && isNaN(obj);
# };
#
# # Is a given value equal to null?
# _.isNull = function(obj) {
# return obj === null;
# };
#
# # Is a given variable undefined?
# _.isUndefined = function(obj) {
# return typeof obj == 'undefined';
# };
#
# # Invokes interceptor with the obj, and then returns obj.
# # The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
# _.tap = function(obj, interceptor) {
# interceptor(obj);
# return obj;
# }
#
# Perform a deep comparison to check if two objects are equal.
_.isEqual: a, b =>
# Check object identity.
return true if a is b
# Different types?
atype: typeof(a); btype: typeof(b)
return false if atype isnt btype
# Basic equality test (watch out for coercions).
return true if `a == b`
# One is falsy and the other truthy.
return false if (!a and b) or (a and !b)
# One of them implements an isEqual()?
return a.isEqual(b) if a.isEqual
# Check dates' integer values.
return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
# Both are NaN?
return true if _.isNaN(a) and _.isNaN(b)
# Compare regular expressions.
if _.isRegExp(a) and _.isRegExp(b)
return a.source is b.source and \
a.global is b.global and \
a.ignoreCase is b.ignoreCase and \
a.multiline is b.multiline.
# If a is not an object by this point, we can't handle it.
return false if atype isnt 'object'
# Check for different array lengths before comparing contents.
return false if a.length and (a.length isnt b.length)
# Nothing else worked, deep compare the contents.
aKeys: _.keys(a); bKeys: _.keys(b)
# Different object sizes?
return false if aKeys.length isnt bKeys.length
# Recursive comparison of contents.
# for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
return true.
# Is a given array or object empty?
_.isEmpty: obj => _.keys(obj).length is 0.
# Is a given value a DOM element?
_.isElement: obj => !!(obj and obj.nodeType is 1).
# Is a given variable an arguments object?
_.isArguments: obj => obj and _.isNumber(obj.length) and !_.isArray(obj) and !propertyIsEnumerable.call(obj, 'length').
# Is the given value NaN -- this one is interesting. NaN != NaN, and
# isNaN(undefined) == true, so we make sure it's a number first.
_.isNaN: obj => _.isNumber(obj) and isNaN(obj).
# Is a given value equal to null?
_.isNull: obj => obj is null.
# Is a given variable undefined?
_.isUndefined: obj => typeof obj is 'undefined'.
# Invokes interceptor with the obj, and then returns obj.
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
_.tap: obj, interceptor =>
interceptor(obj)
obj.
# # Define the isArray, isDate, isFunction, isNumber, isRegExp, and isString
# # functions based on their toString identifiers.
# var types = ['Array', 'Date', 'Function', 'Number', 'RegExp', 'String'];
@@ -492,26 +452,21 @@ _.reduceRight: obj, memo, iterator, context =>
# _['is' + types[i]] = function(obj) { return toString.call(obj) == identifier; };
# })();
# }
#
# /* -------------------------- Utility Functions: -------------------------- */
#
# # Run Underscore.js in noConflict mode, returning the '_' variable to its
# # previous owner. Returns a reference to the Underscore object.
# _.noConflict = function() {
# root._ = previousUnderscore;
# return this;
# };
#
# # Keep the identity function around for default iterators.
# _.identity = function(value) {
# return value;
# };
#
# # Break out of the middle of an iteration.
# _.breakLoop = function() {
# throw breaker;
# };
#
# -------------------------- Utility Functions: --------------------------
# Run Underscore.js in noConflict mode, returning the '_' variable to its
# previous owner. Returns a reference to the Underscore object.
_.noConflict: =>
root._: previousUnderscore
this.
# Keep the identity function around for default iterators.
_.identity: value => value.
# Break out of the middle of an iteration.
_.breakLoop: => throw breaker.
# # Generate a unique integer id (unique within the entire client session).
# # Useful for temporary DOM ids.
# var idCounter = 0;
@@ -537,19 +492,19 @@ _.reduceRight: obj, memo, iterator, context =>
# + "');}return p.join('');");
# return data ? fn(data) : fn;
# };
#
# /*------------------------------- Aliases ----------------------------------*/
#
# _.forEach = _.each;
# _.foldl = _.inject = _.reduce;
# _.foldr = _.reduceRight;
# _.filter = _.select;
# _.every = _.all;
# _.some = _.any;
# _.head = _.first;
# _.tail = _.rest;
# _.methods = _.functions;
#
# ------------------------------- Aliases ----------------------------------
_.forEach: _.each
_.foldl: _.inject: _.reduce
_.foldr: _.reduceRight
_.filter: _.select
_.every: _.all
_.some: _.any
_.head: _.first
_.tail: _.rest
_.methods: _.functions
# /*------------------------ Setup the OOP Wrapper: --------------------------*/
#
# # Helper function to continue chaining intermediate results.

View File

@@ -50,16 +50,17 @@
<a href="#aliases">Aliases</a><br />
<a href="#while">While Loops</a><br />
<a href="#array_comprehensions">Array Comprehensions</a><br />
<a href="#slice">Array Slice Literals</a><br />
<a href="#slice">Slicing Arrays with Ranges</a><br />
<a href="#inheritance">Inheritance, and Calling Super from a Subclass</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="#resources">Resources</a><br />
<a href="#contributing">Contributing</a><br />
<a href="#change_log">Change Log</a><br />
</p>
<h2 id="overview">Mini Overview</h2>
<p><i>CoffeeScript on the left, compiled JavaScript output on the right.</i></p>
@@ -86,22 +87,22 @@ math<span class="Keyword">:</span> {
<span class="Comment"><span class="Comment">#</span> Array comprehensions:</span>
cubed_list<span class="Keyword">:</span> math.cube(num) <span class="Keyword">for</span> num <span class="Keyword">in</span> list.
</pre><pre class="idle">
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, __d, cubed_list, list, math, num, number, opposite_day, square;
<span class="Comment"><span class="Comment">//</span> Assignment:</span>
<span class="Storage">var</span> number <span class="Keyword">=</span> <span class="Number">42</span>;
<span class="Storage">var</span> opposite_day <span class="Keyword">=</span> <span class="BuiltInConstant">true</span>;
number <span class="Keyword">=</span> <span class="Number">42</span>;
opposite_day <span class="Keyword">=</span> <span class="BuiltInConstant">true</span>;
<span class="Comment"><span class="Comment">//</span> Conditions:</span>
<span class="Keyword">if</span> (opposite_day) {
number <span class="Keyword">=</span> <span class="Keyword">-</span><span class="Number">42</span>;
}
<span class="Comment"><span class="Comment">//</span> Functions:</span>
<span class="Storage">var</span> <span class="FunctionName">square</span> = <span class="Storage">function</span>(<span class="FunctionArgument">x</span>) {
<span class="FunctionName">square</span> = <span class="Storage">function</span>(<span class="FunctionArgument">x</span>) {
<span class="Keyword">return</span> x <span class="Keyword">*</span> x;
};
<span class="Comment"><span class="Comment">//</span> Arrays:</span>
<span class="Storage">var</span> list <span class="Keyword">=</span> [<span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>];
list <span class="Keyword">=</span> [<span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>];
<span class="Comment"><span class="Comment">//</span> Objects:</span>
<span class="Storage">var</span> math <span class="Keyword">=</span> {
math <span class="Keyword">=</span> {
root: <span class="LibraryClassType">Math</span>.sqrt,
square: square,
<span class="FunctionName">cube</span>: <span class="Storage">function</span>(<span class="FunctionArgument">x</span>) {
@@ -109,30 +110,29 @@ cubed_list<span class="Keyword">:</span> math.cube(num) <span class="Keyword">fo
}
};
<span class="Comment"><span class="Comment">//</span> Array comprehensions:</span>
<span class="Storage">var</span> cubed_list;
<span class="Storage">var</span> __a <span class="Keyword">=</span> list;
<span class="Storage">var</span> __d <span class="Keyword">=</span> [];
<span class="Keyword">for</span> (<span class="Storage">var</span> __b<span class="Keyword">=</span><span class="Number">0</span>, __c<span class="Keyword">=</span>__a.<span class="LibraryConstant">length</span>; __b<span class="Keyword">&lt;</span>__c; __b<span class="Keyword">++</span>) {
<span class="Storage">var</span> num <span class="Keyword">=</span> __a[__b];
__a <span class="Keyword">=</span> list;
__d <span class="Keyword">=</span> [];
<span class="Keyword">for</span> (__b<span class="Keyword">=</span><span class="Number">0</span>, __c<span class="Keyword">=</span>__a.<span class="LibraryConstant">length</span>; __b<span class="Keyword">&lt;</span>__c; __b<span class="Keyword">++</span>) {
num <span class="Keyword">=</span> __a[__b];
__d[__b] <span class="Keyword">=</span> math.cube(num);
}
cubed_list <span class="Keyword">=</span> __d;
</pre><button onclick='javascript:
</pre><button onclick='javascript: var __a, __b, __c, __d, cubed_list, list, math, num, number, opposite_day, square;
// Assignment:
var number = 42;
var opposite_day = true;
number = 42;
opposite_day = true;
// Conditions:
if (opposite_day) {
number = -42;
}
// Functions:
var square = function(x) {
square = function(x) {
return x * x;
};
// Arrays:
var list = [1, 2, 3, 4, 5];
list = [1, 2, 3, 4, 5];
// Objects:
var math = {
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
@@ -140,11 +140,10 @@ var math = {
}
};
// Array comprehensions:
var cubed_list;
var __a = list;
var __d = [];
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var num = __a[__b];
__a = list;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
num = __a[__b];
__d[__b] = math.cube(num);
}
cubed_list = __d;
@@ -161,12 +160,12 @@ cubed_list = __d;
gem install coffee-script</pre>
<p>
Installing the gem provides the <tt>coffee-script</tt> command, which can
Installing the gem provides the <tt>coffee</tt> command, which can
be used to compile CoffeeScript <tt>.coffee</tt> files into JavaScript, as
well as debug them. In conjunction with
<a href="http://narwhaljs.org/">Narwhal</a>, the <tt>coffee-script</tt>
<a href="http://narwhaljs.org/">Narwhal</a>, the <tt>coffee</tt>
command also provides direct evaluation and an interactive REPL.
When compiling to JavaScript, <tt>coffee-script</tt> writes the output
When compiling to JavaScript, <tt>coffee</tt> writes the output
as <tt>.js</tt> files in the same directory by default, but output
can be customized with the following options:
</p>
@@ -218,7 +217,7 @@ gem install coffee-script</pre>
<td><code>-e, --eval</code></td>
<td>
Compile and print a little snippet of CoffeeScript directly from the
command line (or from <b>stdin</b>). For example:<br /><tt>coffee-script -e "square: x => x * x."</tt>
command line (or from <b>stdin</b>). For example:<br /><tt>coffee -e "square: x => x * x."</tt>
</td>
</tr>
<tr>
@@ -257,10 +256,10 @@ gem install coffee-script</pre>
</p>
<pre>
coffee-script path/to/script.coffee
coffee-script --interactive
coffee-script --watch --lint experimental.coffee
coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
coffee path/to/script.coffee
coffee --interactive
coffee --watch --lint experimental.coffee
coffee --print app/scripts/*.coffee > concatenation.js</pre>
<h2>Language Reference</h2>
@@ -293,16 +292,18 @@ coffee-script --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<div class='code'><pre class="idle"><span class="FunctionName">square</span><span class="Keyword">:</span> <span class="FunctionArgument">x</span> <span class="Storage">=&gt;</span> x <span class="Keyword">*</span> x.
<span class="FunctionName">cube</span><span class="Keyword">:</span> <span class="FunctionArgument">x</span> <span class="Storage">=&gt;</span> square(x) <span class="Keyword">*</span> x.
</pre><pre class="idle"><span class="Storage">var</span> <span class="FunctionName">square</span> = <span class="Storage">function</span>(<span class="FunctionArgument">x</span>) {
</pre><pre class="idle"><span class="Storage">var</span> cube, square;
<span class="FunctionName">square</span> = <span class="Storage">function</span>(<span class="FunctionArgument">x</span>) {
<span class="Keyword">return</span> x <span class="Keyword">*</span> x;
};
<span class="Storage">var</span> <span class="FunctionName">cube</span> = <span class="Storage">function</span>(<span class="FunctionArgument">x</span>) {
<span class="FunctionName">cube</span> = <span class="Storage">function</span>(<span class="FunctionArgument">x</span>) {
<span class="Keyword">return</span> square(x) <span class="Keyword">*</span> x;
};
</pre><button onclick='javascript: var square = function(x) {
</pre><button onclick='javascript: var cube, square;
square = function(x) {
return x * x;
};
var cube = function(x) {
cube = function(x) {
return square(x) * x;
};
;alert(cube(5));'>run: cube(5)</button><br class='clear' /></div>
@@ -315,11 +316,17 @@ var cube = function(x) {
</p>
<div class='code'><pre class="idle">greeting<span class="Keyword">:</span> <span class="String"><span class="String">&quot;</span>Hello CoffeeScript<span class="String">&quot;</span></span>
difficulty<span class="Keyword">:</span> <span class="Number">0.5</span>
</pre><pre class="idle"><span class="Storage">var</span> greeting <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>Hello CoffeeScript<span class="String">&quot;</span></span>;
<span class="Storage">var</span> difficulty <span class="Keyword">=</span> <span class="Number">0.5</span>;
</pre><button onclick='javascript: var greeting = "Hello CoffeeScript";
var difficulty = 0.5;
</pre><pre class="idle"><span class="Storage">var</span> difficulty, greeting;
greeting <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>Hello CoffeeScript<span class="String">&quot;</span></span>;
difficulty <span class="Keyword">=</span> <span class="Number">0.5</span>;
</pre><button onclick='javascript: var difficulty, greeting;
greeting = "Hello CoffeeScript";
difficulty = 0.5;
;alert(greeting);'>run: greeting</button><br class='clear' /></div>
<p>
Declarations of new variables are pushed up to the top of the current scope,
so that assignments may always be used within expressions.
</p>
<p id="objects_and_arrays">
<b class="header">Objects and Arrays</b>
@@ -334,14 +341,16 @@ ages<span class="Keyword">:</span> {
ida<span class="Keyword">:</span> <span class="Number">9</span>
tim<span class="Keyword">:</span> <span class="Number">11</span>
}
</pre><pre class="idle"><span class="Storage">var</span> song <span class="Keyword">=</span> [<span class="String"><span class="String">&quot;</span>do<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>re<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>mi<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>fa<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>so<span class="String">&quot;</span></span>];
<span class="Storage">var</span> ages <span class="Keyword">=</span> {
</pre><pre class="idle"><span class="Storage">var</span> ages, song;
song <span class="Keyword">=</span> [<span class="String"><span class="String">&quot;</span>do<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>re<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>mi<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>fa<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>so<span class="String">&quot;</span></span>];
ages <span class="Keyword">=</span> {
max: <span class="Number">10</span>,
ida: <span class="Number">9</span>,
tim: <span class="Number">11</span>
};
</pre><button onclick='javascript: var song = ["do", "re", "mi", "fa", "so"];
var ages = {
</pre><button onclick='javascript: var ages, song;
song = ["do", "re", "mi", "fa", "so"];
ages = {
max: 10,
ida: 9,
tim: 11
@@ -359,20 +368,22 @@ var ages = {
num<span class="Keyword">:</span> <span class="Number">2</span>
new_num<span class="Keyword">:</span> <span class="Number">3</span>.
new_num<span class="Keyword">:</span> change_numbers()
</pre><pre class="idle"><span class="Storage">var</span> num <span class="Keyword">=</span> <span class="Number">1</span>;
<span class="Storage">var</span> <span class="FunctionName">change_numbers</span> = <span class="Storage">function</span>() {
</pre><pre class="idle"><span class="Storage">var</span> change_numbers, new_num, num;
num <span class="Keyword">=</span> <span class="Number">1</span>;
<span class="FunctionName">change_numbers</span> = <span class="Storage">function</span>() {
<span class="Storage">var</span> new_num;
num <span class="Keyword">=</span> <span class="Number">2</span>;
<span class="Storage">var</span> new_num <span class="Keyword">=</span> <span class="Number">3</span>;
<span class="Keyword">return</span> new_num;
<span class="Keyword">return</span> (new_num <span class="Keyword">=</span> <span class="Number">3</span>);
};
<span class="Storage">var</span> new_num <span class="Keyword">=</span> change_numbers();
</pre><button onclick='javascript: var num = 1;
var change_numbers = function() {
new_num <span class="Keyword">=</span> change_numbers();
</pre><button onclick='javascript: var change_numbers, new_num, num;
num = 1;
change_numbers = function() {
var new_num;
num = 2;
var new_num = 3;
return new_num;
return (new_num = 3);
};
var new_num = change_numbers();
new_num = change_numbers();
;alert(new_num);'>run: new_num</button><br class='clear' /></div>
<p>
Notice how the variables are declared with <tt>var</tt> the first time
@@ -408,7 +419,7 @@ var new_num = change_numbers();
date<span class="Keyword">:</span> <span class="Keyword">if</span> friday <span class="Keyword">then</span> sue <span class="Keyword">else</span> jill.
expensive <span class="Keyword">||</span><span class="Keyword">=</span> do_the_math()
</pre><pre class="idle"><span class="Storage">var</span> mood;
</pre><pre class="idle"><span class="Storage">var</span> date, mood;
<span class="Keyword">if</span> (singing) {
mood <span class="Keyword">=</span> greatly_improved;
}
@@ -416,7 +427,7 @@ expensive <span class="Keyword">||</span><span class="Keyword">=</span> do_the_m
claps_hands();
cha_cha_cha();
}
<span class="Storage">var</span> date <span class="Keyword">=</span> friday ? sue : jill;
date <span class="Keyword">=</span> friday ? sue : jill;
expensive <span class="Keyword">=</span> expensive <span class="Keyword">||</span> do_the_math();
</pre><br class='clear' /></div>
<p>
@@ -444,7 +455,8 @@ expensive <span class="Keyword">=</span> expensive <span class="Keyword">||</spa
<span class="String"><span class="String">&quot;</span>C<span class="String">&quot;</span></span>..
eldest<span class="Keyword">:</span> <span class="Keyword">if</span> <span class="Number">24</span> <span class="Keyword">&gt;</span> <span class="Number">21</span> <span class="Keyword">then</span> <span class="String"><span class="String">&quot;</span>Liz<span class="String">&quot;</span></span> <span class="Keyword">else</span> <span class="String"><span class="String">&quot;</span>Ike<span class="String">&quot;</span></span>.
</pre><pre class="idle"><span class="Storage">var</span> <span class="FunctionName">grade</span> = <span class="Storage">function</span>(<span class="FunctionArgument">student</span>) {
</pre><pre class="idle"><span class="Storage">var</span> eldest, grade;
<span class="FunctionName">grade</span> = <span class="Storage">function</span>(<span class="FunctionArgument">student</span>) {
<span class="Keyword">if</span> (student.excellent_work) {
<span class="Keyword">return</span> <span class="String"><span class="String">&quot;</span>A+<span class="String">&quot;</span></span>;
} <span class="Keyword">else</span> <span class="Keyword">if</span> (student.okay_stuff) {
@@ -453,8 +465,9 @@ eldest<span class="Keyword">:</span> <span class="Keyword">if</span> <span class
<span class="Keyword">return</span> <span class="String"><span class="String">&quot;</span>C<span class="String">&quot;</span></span>;
}
};
<span class="Storage">var</span> eldest <span class="Keyword">=</span> <span class="Number">24</span> <span class="Keyword">&gt;</span> <span class="Number">21</span> ? <span class="String"><span class="String">&quot;</span>Liz<span class="String">&quot;</span></span> : <span class="String"><span class="String">&quot;</span>Ike<span class="String">&quot;</span></span>;
</pre><button onclick='javascript: var grade = function(student) {
eldest <span class="Keyword">=</span> <span class="Number">24</span> <span class="Keyword">&gt;</span> <span class="Number">21</span> ? <span class="String"><span class="String">&quot;</span>Liz<span class="String">&quot;</span></span> : <span class="String"><span class="String">&quot;</span>Ike<span class="String">&quot;</span></span>;
</pre><button onclick='javascript: var eldest, grade;
grade = function(student) {
if (student.excellent_work) {
return "A+";
} else if (student.okay_stuff) {
@@ -463,12 +476,22 @@ eldest<span class="Keyword">:</span> <span class="Keyword">if</span> <span class
return "C";
}
};
var eldest = 24 > 21 ? "Liz" : "Ike";
eldest = 24 > 21 ? "Liz" : "Ike";
;alert(eldest);'>run: eldest</button><br class='clear' /></div>
<p>
The same mechanism is used to push down assignment through <b>switch</b>
statements, and <b>if-elses</b> (although the ternary operator is preferred).
Another part of manipulating assignment statements is
CoffeeScript's declaration of new variables at the top of the
current scope. This allows assignment to be used as a piece of an
expression.
</p>
<div class='code'><pre class="idle">six<span class="Keyword">:</span> (one<span class="Keyword">:</span> <span class="Number">1</span>) <span class="Keyword">+</span> (two<span class="Keyword">:</span> <span class="Number">2</span>) <span class="Keyword">+</span> (three<span class="Keyword">:</span> <span class="Number">3</span>)
</pre><pre class="idle"><span class="Storage">var</span> one, six, three, two;
six <span class="Keyword">=</span> (one <span class="Keyword">=</span> <span class="Number">1</span>) <span class="Keyword">+</span> (two <span class="Keyword">=</span> <span class="Number">2</span>) <span class="Keyword">+</span> (three <span class="Keyword">=</span> <span class="Number">3</span>);
</pre><button onclick='javascript: var one, six, three, two;
six = (one = 1) + (two = 2) + (three = 3);
;alert(six);'>run: six</button><br class='clear' /></div>
<p id="aliases">
<b class="header">Aliases</b>
@@ -505,10 +528,10 @@ volume<span class="Keyword">:</span> <span class="Number">10</span> <span class=
let_the_wild_rumpus_begin() <span class="Keyword">unless</span> answer <span class="Keyword">is</span> <span class="BuiltInConstant">no</span>
<span class="Keyword">if</span> car.speed <span class="Keyword">&lt;</span> speed_limit <span class="Keyword">then</span> accelerate().
</pre><pre class="idle"><span class="Keyword">if</span> (ignition <span class="Keyword">===</span> <span class="BuiltInConstant">true</span>) {
</pre><pre class="idle"><span class="Storage">var</span> volume;
<span class="Keyword">if</span> (ignition <span class="Keyword">===</span> <span class="BuiltInConstant">true</span>) {
launch();
}
<span class="Storage">var</span> volume;
<span class="Keyword">if</span> (band <span class="Keyword">!</span><span class="Keyword">==</span> spinal_tap) {
volume <span class="Keyword">=</span> <span class="Number">10</span>;
}
@@ -556,40 +579,55 @@ lunch<span class="Keyword">:</span> food.eat() <span class="Keyword">for</span>
<span class="Comment"><span class="Comment">#</span> Zebra-stripe a table.</span>
highlight(row) <span class="Keyword">for</span> row, i <span class="Keyword">in</span> table <span class="Keyword">if</span> i <span class="Keyword">%</span> <span class="Number">2</span> <span class="Keyword">is</span> <span class="Number">0</span>.
</pre><pre class="idle">
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, __d, __e, __f, __g, __h, food, i, lunch, row;
<span class="Comment"><span class="Comment">//</span> Eat lunch.</span>
<span class="Storage">var</span> lunch;
<span class="Storage">var</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="Storage">var</span> __d <span class="Keyword">=</span> [];
<span class="Keyword">for</span> (<span class="Storage">var</span> __b<span class="Keyword">=</span><span class="Number">0</span>, __c<span class="Keyword">=</span>__a.<span class="LibraryConstant">length</span>; __b<span class="Keyword">&lt;</span>__c; __b<span class="Keyword">++</span>) {
<span class="Storage">var</span> food <span class="Keyword">=</span> __a[__b];
__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>];
__d <span class="Keyword">=</span> [];
<span class="Keyword">for</span> (__b<span class="Keyword">=</span><span class="Number">0</span>, __c<span class="Keyword">=</span>__a.<span class="LibraryConstant">length</span>; __b<span class="Keyword">&lt;</span>__c; __b<span class="Keyword">++</span>) {
food <span class="Keyword">=</span> __a[__b];
__d[__b] <span class="Keyword">=</span> food.eat();
}
lunch <span class="Keyword">=</span> __d;
<span class="Comment"><span class="Comment">//</span> Zebra-stripe a table.</span>
<span class="Storage">var</span> __e <span class="Keyword">=</span> table;
<span class="Storage">var</span> __h <span class="Keyword">=</span> [];
<span class="Keyword">for</span> (<span class="Storage">var</span> __f<span class="Keyword">=</span><span class="Number">0</span>, __g<span class="Keyword">=</span>__e.<span class="LibraryConstant">length</span>; __f<span class="Keyword">&lt;</span>__g; __f<span class="Keyword">++</span>) {
<span class="Storage">var</span> row <span class="Keyword">=</span> __e[__f];
<span class="Storage">var</span> i <span class="Keyword">=</span> __f;
__e <span class="Keyword">=</span> table;
__h <span class="Keyword">=</span> [];
<span class="Keyword">for</span> (__f<span class="Keyword">=</span><span class="Number">0</span>, __g<span class="Keyword">=</span>__e.<span class="LibraryConstant">length</span>; __f<span class="Keyword">&lt;</span>__g; __f<span class="Keyword">++</span>) {
row <span class="Keyword">=</span> __e[__f];
i <span class="Keyword">=</span> __f;
__h[__f] <span class="Keyword">=</span> i <span class="Keyword">%</span> <span class="Number">2</span> <span class="Keyword">===</span> <span class="Number">0</span> ? highlight(row) : <span class="BuiltInConstant">null</span>;
}
__h;
</pre><br class='clear' /></div>
<p>
If you're not iterating over an actual array, you can use a range to
specify the start and end of an array comprehension:
<tt>coundown(i) for i in [10..1].</tt>
</p>
<p id="slice">
<b class="header">Array Slice Literals</b>
CoffeeScript includes syntax for extracting slices of arrays.
The first argument is the index of the first element in the slice, and
the second is the index of the last one.
<b class="header">Slicing Arrays with Ranges</b>
CoffeeScript borrows Ruby's
<a href="http://ruby-doc.org/core/classes/Range.html">range syntax</a>
for extracting slices of arrays. With two dots (<tt>3..5</tt>), the range
is inclusive: the first argument is the index of the first element in
the slice, and the second is the index of the last one. Three dots signify
a range that excludes the end.
</p>
<div class='code'><pre class="idle">nums<span class="Keyword">:</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>]
three_to_six<span class="Keyword">:</span> nums[<span class="Number">3</span>, <span class="Number">6</span>]
</pre><pre class="idle"><span class="Storage">var</span> nums <span class="Keyword">=</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>];
<span class="Storage">var</span> three_to_six <span class="Keyword">=</span> nums.<span class="LibraryFunction">slice</span>(<span class="Number">3</span>, <span class="Number">6</span> <span class="Keyword">+</span> <span class="Number">1</span>);
</pre><button onclick='javascript: var nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var three_to_six = nums.slice(3, 6 + 1);
;alert(three_to_six);'>run: three_to_six</button><br class='clear' /></div>
<div class='code'><pre class="idle">numbers<span class="Keyword">:</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>]
three_to_six<span class="Keyword">:</span> numbers[<span class="Number">3</span>..<span class="Number">6</span>]
numbers_copy<span class="Keyword">:</span> numbers[<span class="Number">0</span>...numbers.length]
</pre><pre class="idle"><span class="Storage">var</span> numbers, numbers_copy, three_to_six;
numbers <span class="Keyword">=</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>];
three_to_six <span class="Keyword">=</span> numbers.<span class="LibraryFunction">slice</span>(<span class="Number">3</span>, <span class="Number">6</span> <span class="Keyword">+</span> <span class="Number">1</span>);
numbers_copy <span class="Keyword">=</span> numbers.<span class="LibraryFunction">slice</span>(<span class="Number">0</span>, numbers.<span class="LibraryConstant">length</span>);
</pre><button onclick='javascript: var numbers, numbers_copy, three_to_six;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
three_to_six = numbers.slice(3, 6 + 1);
numbers_copy = numbers.slice(0, numbers.length);
;alert(numbers_copy);'>run: numbers_copy</button><br class='clear' /></div>
<p id="inheritance">
<b class="header">Inheritance, and Calling Super from a Subclass</b>
@@ -633,14 +671,14 @@ tom.move()
</pre><pre class="idle"><span class="Storage">var</span> <span class="FunctionName">Animal</span> = <span class="Storage">function</span>() {
</pre><pre class="idle"><span class="Storage">var</span> Animal, Horse, Snake, sam, tom;
<span class="FunctionName">Animal</span> = <span class="Storage">function</span>() {
};
<span class="LibraryClassType">Animal</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">move</span> = <span class="Storage">function</span>(<span class="FunctionArgument">meters</span>) {
<span class="Keyword">return</span> <span class="LibraryFunction">alert</span>(<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>);
};
<span class="Storage">var</span> <span class="FunctionName">Snake</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name;
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>;
<span class="FunctionName">Snake</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
<span class="Keyword">return</span> (<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name);
};
Snake.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryConstant">prototype</span>;
<span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
@@ -649,9 +687,8 @@ Snake.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryC
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>);
<span class="Keyword">return</span> Snake.__superClass__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">5</span>);
};
<span class="Storage">var</span> <span class="FunctionName">Horse</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name;
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>;
<span class="FunctionName">Horse</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
<span class="Keyword">return</span> (<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name);
};
Horse.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryConstant">prototype</span>;
<span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
@@ -660,18 +697,18 @@ Horse.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryC
<span class="LibraryFunction">alert</span>(<span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>);
<span class="Keyword">return</span> Horse.__superClass__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">45</span>);
};
<span class="Storage">var</span> sam <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Snake</span>(<span class="String"><span class="String">&quot;</span>Sammy the Python<span class="String">&quot;</span></span>);
<span class="Storage">var</span> tom <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Horse</span>(<span class="String"><span class="String">&quot;</span>Tommy the Palomino<span class="String">&quot;</span></span>);
sam <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Snake</span>(<span class="String"><span class="String">&quot;</span>Sammy the Python<span class="String">&quot;</span></span>);
tom <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Horse</span>(<span class="String"><span class="String">&quot;</span>Tommy the Palomino<span class="String">&quot;</span></span>);
sam.move();
tom.move();
</pre><button onclick='javascript: var Animal = function() {
</pre><button onclick='javascript: var Animal, Horse, Snake, sam, tom;
Animal = function() {
};
Animal.prototype.move = function(meters) {
return alert(this.name + " moved " + meters + "m.");
};
var Snake = function(name) {
this.name = name;
return this.name;
Snake = function(name) {
return (this.name = name);
};
Snake.__superClass__ = Animal.prototype;
Snake.prototype = new Animal();
@@ -680,9 +717,8 @@ Snake.prototype.move = function() {
alert("Slithering...");
return Snake.__superClass__.move.call(this, 5);
};
var Horse = function(name) {
this.name = name;
return this.name;
Horse = function(name) {
return (this.name = name);
};
Horse.__superClass__ = Animal.prototype;
Horse.prototype = new Animal();
@@ -691,8 +727,8 @@ Horse.prototype.move = function() {
alert("Galloping...");
return Horse.__superClass__.move.call(this, 45);
};
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");
sam = new Snake("Sammy the Python");
tom = new Horse("Tommy the Palomino");
sam.move();
tom.move();
;'>run</button><br class='clear' /></div>
@@ -706,10 +742,13 @@ tom.move();
<span class="String"> return [document.title, &quot;Hello JavaScript&quot;].join(&quot;: &quot;);</span>
<span class="String">}<span class="String">`</span></span>
</pre><pre class="idle"><span class="Storage">var</span> <span class="FunctionName">hi</span> = <span class="Storage">function</span>() {
</pre><pre class="idle"><span class="Storage">var</span> hi;
<span class="FunctionName">hi</span> = <span class="Storage">function</span>() {
<span class="Keyword">return</span> [<span class="LibraryClassType">document</span>.<span class="LibraryConstant">title</span>, <span class="String"><span class="String">&quot;</span>Hello JavaScript<span class="String">&quot;</span></span>].<span class="LibraryFunction">join</span>(<span class="String"><span class="String">&quot;</span>: <span class="String">&quot;</span></span>);
};
</pre><button onclick='javascript: var hi = function() {
</pre><button onclick='javascript: var hi;
hi = function() {
return [document.title, "Hello JavaScript"].join(": ");
};
;alert(hi());'>run: hi()</button><br class='clear' /></div>
@@ -782,19 +821,29 @@ when <span class="String"><span class="String">&quot;</span>Sunday<span class="S
<span class="String">about a little and see the watery part of the</span>
<span class="String">world...<span class="String">&quot;</span></span>
</pre><pre class="idle"><span class="Storage">var</span> moby_dick <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>Call me Ishmael. Some years ago -- \</span>
</pre><pre class="idle"><span class="Storage">var</span> moby_dick;
moby_dick <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>Call me Ishmael. Some years ago -- \</span>
<span class="String">never mind how long precisely -- having little \</span>
<span class="String">or no money in my purse, and nothing particular \</span>
<span class="String">to interest me on shore, I thought I would sail \</span>
<span class="String">about a little and see the watery part of the \</span>
<span class="String">world...<span class="String">&quot;</span></span>;
</pre><button onclick='javascript: var moby_dick = "Call me Ishmael. Some years ago -- \
</pre><button onclick='javascript: var moby_dick;
moby_dick = "Call me Ishmael. Some years ago -- \
never mind how long precisely -- having little \
or no money in my purse, and nothing particular \
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>
<h2 id="resources">Resources</h2>
<p>
<a href="http://github.com/jashkenas/coffee-script/">Source Code</a><br />
<a href="http://github.com/jashkenas/coffee-script/issues">Bugs and Feature Requests</a><br />
</p>
<h2 id="contributing">Contributing</h2>
@@ -810,7 +859,8 @@ world...";
</li>
<li>
Ideas for alternate syntax to end blocks of expressions &mdash; the periods
can look a little weird with deeply nested structure.
can look a little weird with deeply nested structure. (There's now a
'whitespace' branch &mdash; help add significant whitespace over there.)
</li>
<li>
Test cases for any syntax errors you encounter that you think CoffeeScript
@@ -833,21 +883,37 @@ world...";
<h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 20px;">0.1.6</b>
Bugfix for running <tt>coffee --interactive</tt> and <tt>--run</tt>
from outside of the CoffeeScript directory. Bugfix for nested
function/if-statements.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.5</b>
Array slice literals and array comprehensions can now both take Ruby-style
ranges to specify the start and end. JavaScript variable declaration is
now pushed up to the top of the scope, making all assignment statements into
expressions. You can use <tt>\</tt> to escape newlines.
The <tt>coffee-script</tt> command is now called <tt>coffee</tt>.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.4</b>
The official CoffeeScript extension is now <tt>.coffee</tt> instead of
<tt>.cs</tt>, which properly belongs to
<tt>.cs</tt>, which properly belongs to
<a href="http://en.wikipedia.org/wiki/C_Sharp_(programming_language)">C#</a>.
Due to popular demand, you can now also use <tt>=</tt> to assign. Unlike
JavaScript, <tt>=</tt> can also be used within object literals, interchangeably
with <tt>:</tt>. Made a grammatical fix for chained function calls
like <tt>func(1)(2)(3)(4)</tt>. Inheritance and super no longer use
<tt>__proto__</tt>, so they should be IE-compatible now.
with <tt>:</tt>. Made a grammatical fix for chained function calls
like <tt>func(1)(2)(3)(4)</tt>. Inheritance and super no longer use
<tt>__proto__</tt>, so they should be IE-compatible now.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.3</b>
The <tt>coffee-script</tt> command now includes <tt>--interactive</tt>,
The <tt>coffee</tt> command now includes <tt>--interactive</tt>,
which launches an interactive CoffeeScript session, and <tt>--run</tt>,
which directly compiles and executes a script. Both options depend on a
working installation of Narwhal.

View File

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

View File

@@ -234,7 +234,7 @@
</dict>
<dict>
<key>match</key>
<string>\b(debugger)\b</string>
<string>\b(debugger|\\)\b</string>
<key>name</key>
<string>keyword.other.coffee</string>
</dict>

View File

@@ -5,20 +5,23 @@ require File.expand_path(File.dirname(__FILE__) + '/../coffee-script')
module CoffeeScript
# The CommandLine handles all of the functionality of the `coffee-script`
# The CommandLine handles all of the functionality of the `coffee`
# utility.
class CommandLine
BANNER = <<-EOS
coffee-script compiles CoffeeScript source files into JavaScript.
coffee compiles CoffeeScript source files into JavaScript.
Usage:
coffee-script path/to/script.coffee
coffee path/to/script.coffee
EOS
# Seconds to pause between checks for changed source files.
WATCH_INTERVAL = 0.5
# Path to the Narwhal Launcher:
LAUNCHER = File.expand_path(File.dirname(__FILE__)) + '/narwhal/js/launcher.js'
# Run the CommandLine off the contents of ARGV.
def initialize
@mtimes = {}
@@ -104,7 +107,7 @@ Usage:
# Use Narwhal to run an interactive CoffeeScript session.
def launch_repl
exec "narwhal lib/coffee_script/narwhal/js/launcher.js"
exec "narwhal #{LAUNCHER}"
rescue Errno::ENOENT
puts "Error: Narwhal must be installed to use the interactive REPL."
exit(1)
@@ -113,7 +116,7 @@ Usage:
# Use Narwhal to compile and execute CoffeeScripts.
def run_scripts
sources = @sources.join(' ')
exec "narwhal lib/coffee_script/narwhal/js/launcher.js #{sources}"
exec "narwhal #{LAUNCHER} #{sources}"
rescue Errno::ENOENT
puts "Error: Narwhal must be installed in order to execute CoffeeScripts."
exit(1)
@@ -125,11 +128,13 @@ Usage:
end
# Compile a single source file to JavaScript.
def compile(script, source='')
def compile(script, source='error')
begin
CoffeeScript.compile(script, :no_wrap => @options[:no_wrap])
rescue CoffeeScript::ParseError => e
STDERR.puts e.message(source)
options = {}
options[:no_wrap] = true if @options[:no_wrap]
CoffeeScript.compile(script, options)
rescue CoffeeScript::ParseError, SyntaxError => e
STDERR.puts "#{source}: #{e.message}"
exit(1) unless @options[:watch]
nil
end
@@ -188,8 +193,8 @@ Usage:
install_bundle
exit
end
opts.on_tail('--version', 'display coffee-script version') do
puts "coffee-script version #{CoffeeScript::VERSION}"
opts.on_tail('--version', 'display CoffeeScript version') do
puts "CoffeeScript version #{CoffeeScript::VERSION}"
exit
end
opts.on_tail('-h', '--help', 'display this help message') do

View File

@@ -64,11 +64,11 @@ rule
# The parts that are natural JavaScript expressions.
PureExpression:
Literal
| Value
Value
| Call
| Code
| Operation
| Range
;
# We have to take extra care to convert these statements into expressions.
@@ -120,8 +120,8 @@ rule
# Assignment within an object literal.
AssignObj:
IDENTIFIER ASSIGN Expression { result = AssignNode.new(val[0], val[2], :object) }
| STRING ASSIGN Expression { result = AssignNode.new(val[0], val[2], :object) }
IDENTIFIER ASSIGN Expression { result = AssignNode.new(ValueNode.new(val[0]), val[2], :object) }
| STRING ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
| Comment { result = val[0] }
;
@@ -144,10 +144,10 @@ rule
| '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
| NOT Expression { result = OpNode.new(val[0], val[1]) }
| '~' Expression { result = OpNode.new(val[0], val[1]) }
| '--' Expression { result = OpNode.new(val[0], val[1]) }
| '++' Expression { result = OpNode.new(val[0], val[1]) }
| Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
| Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
| '--' Expression { result = OpNode.new(val[0], val[1]) }
| '++' Expression { result = OpNode.new(val[0], val[1]) }
| Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
| Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
| Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
@@ -213,6 +213,7 @@ rule
# Expressions that can be treated as values.
Value:
IDENTIFIER { result = ValueNode.new(val[0]) }
| Literal { result = ValueNode.new(val[0]) }
| Array { result = ValueNode.new(val[0]) }
| Object { result = ValueNode.new(val[0]) }
| Parenthetical { result = ValueNode.new(val[0]) }
@@ -224,7 +225,7 @@ rule
Accessor:
PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
| Index { result = val[0] }
| Slice { result = val[0] }
| Range { result = SliceNode.new(val[0]) }
;
# Indexing into an object or array.
@@ -232,11 +233,6 @@ rule
"[" Expression "]" { result = IndexNode.new(val[1]) }
;
# Array slice literal.
Slice:
"[" Expression "," Expression "]" { result = SliceNode.new(val[1], val[3]) }
;
# An object literal.
Object:
"{" AssignList "}" { result = ObjectNode.new(val[1]) }
@@ -273,6 +269,12 @@ rule
SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
;
# The range literal.
Range:
"[" Value "." "." Value "]" { result = RangeNode.new(val[1], val[4]) }
| "[" Value "." "." "." Value "]" { result = RangeNode.new(val[1], val[5], true) }
;
# The array literal.
Array:
"[" ArgList "]" { result = ArrayNode.new(val[1]) }

View File

@@ -19,7 +19,7 @@ module CoffeeScript
# Token matching regexes.
IDENTIFIER = /\A([a-zA-Z$_]\w*)/
NUMBER = /\A\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?))\b/i
NUMBER = /\A((\b|-)((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
STRING = /\A(""|''|"(.*?)[^\\]"|'(.*?)[^\\]')/m
JS = /\A(``|`(.*?)[^\\]`)/m
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
@@ -75,7 +75,7 @@ module CoffeeScript
# Keywords are special identifiers tagged with their own name, 'if' will result
# in an [:IF, "if"] token
tag = KEYWORDS.include?(identifier) ? identifier.upcase.to_sym : :IDENTIFIER
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.'
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2][1] == '.')
token(tag, identifier)
@i += identifier.length
end
@@ -130,11 +130,13 @@ module CoffeeScript
# We treat all other single characters as a token. Eg.: ( ) , . !
# Multi-character operators are also literal tokens, so that Racc can assign
# the proper order of operations. Multiple newlines get merged together.
# Use a trailing \ to escape newlines.
def literal_token
value = @chunk[NEWLINE, 1]
if value
@line += value.length
token("\n", "\n") unless last_value == "\n"
token("\n", "\n") unless ["\n", "\\"].include?(last_value)
@tokens.pop if last_value == "\\"
return @i += value.length
end
value = @chunk[OPERATOR, 1]

View File

@@ -1,6 +1,6 @@
# This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee
# Executes the `coffee-script` Ruby program to convert from CoffeeScript
# Executes the `coffee` Ruby program to convert from CoffeeScript
# to Javascript. Eventually this will hopefully happen entirely within JS.
# Require external dependencies.
@@ -9,23 +9,25 @@ File: require('file')
Readline: require('readline')
# The path to the CoffeeScript Compiler.
coffeePath: File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee-script')
coffeePath: File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee')
# Our general-purpose error handler.
checkForErrors: coffeeProcess =>
return true if coffeeProcess.wait() is 0
system.stderr.print(coffeeProcess.stderr.read())
throw new Error("coffee-script compile error").
throw new Error("CoffeeScript compile error").
# Run a simple REPL, round-tripping to the CoffeeScript compiler for every
# command.
exports.run: args =>
args.shift()
return require(File.absolute(args[0])) if args.length
if args.length
exports.evalCS(File.read(path)) for path in args.
return true.
while true
try
system.stdout.write('cs> ').flush()
system.stdout.write('coffee> ').flush()
result: exports.evalCS(Readline.readline())
print(result) if result isnt undefined
catch e
@@ -51,7 +53,7 @@ exports.evalCS: source =>
# Make a factory for the CoffeeScript environment.
exports.makeNarwhalFactory: path =>
code: exports.compileFile(path)
factoryText: "function(require,exports,module,system,print){" + code + "/**/\n}"
factoryText: "function(require,exports,module,system,print){ 1 + 1 /**/\n}"
if system.engine is "rhino"
Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null)
else

View File

@@ -1,31 +1,39 @@
(function(){
// This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee Executes the `coffee-script` Ruby program to convert from CoffeeScript
var File, OS, Readline, checkForErrors, coffeePath;
// This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee Executes the `coffee` Ruby program to convert from CoffeeScript
// to Javascript. Eventually this will hopefully happen entirely within JS. Require external dependencies.
var OS = require('os');
var File = require('file');
var Readline = require('readline');
OS = require('os');
File = require('file');
Readline = require('readline');
// The path to the CoffeeScript Compiler.
var coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee-script');
coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee');
// Our general-purpose error handler.
var checkForErrors = function(coffeeProcess) {
checkForErrors = function(coffeeProcess) {
if (coffeeProcess.wait() === 0) {
return true;
}
system.stderr.print(coffeeProcess.stderr.read());
throw new Error("coffee-script compile error");
throw new Error("CoffeeScript compile error");
};
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every
// command.
exports.run = function(args) {
var __a, __b, __c, __d, path, result;
args.shift();
if (args.length) {
return require(File.absolute(args[0]));
__a = args;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
path = __a[__b];
__d[__b] = exports.evalCS(File.read(path));
}
__d;
return true;
}
while (true) {
try {
system.stdout.write('cs> ').flush();
var result = exports.evalCS(Readline.readline());
system.stdout.write('coffee> ').flush();
result = exports.evalCS(Readline.readline());
if (result !== undefined) {
print(result);
}
@@ -36,13 +44,15 @@
};
// Compile a given CoffeeScript file into JavaScript.
exports.compileFile = function(path) {
var coffee = OS.popen([coffeePath, "--print", "--no-wrap", path]);
var coffee;
coffee = OS.popen([coffeePath, "--print", "--no-wrap", path]);
checkForErrors(coffee);
return coffee.stdout.read();
};
// Compile a string of CoffeeScript into JavaScript.
exports.compile = function(source) {
var coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
var coffee;
coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
coffee.stdin.write(source).flush().close();
checkForErrors(coffee);
return coffee.stdout.read();
@@ -53,8 +63,9 @@
};
// Make a factory for the CoffeeScript environment.
exports.makeNarwhalFactory = function(path) {
var code = exports.compileFile(path);
var factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
var code, factoryText;
code = exports.compileFile(path);
factoryText = "function(require,exports,module,system,print){ 1 + 1 /**/\n}";
if (system.engine === "rhino") {
return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
} else {

View File

@@ -1,3 +1,3 @@
(function(){
require("coffee-script").run(system.args);
require("./coffee-script").run(system.args);
})();

View File

@@ -1,15 +1,14 @@
(function(){
var coffeescript, factories, loader;
// This (javascript) file is generated from lib/coffee_script/narwhal/loader.coffee
var coffeescript = null;
var factories = {
coffeescript = null;
factories = {
};
var loader = {
loader = {
// Reload the coffee-script environment from source.
reload: function(topId, path) {
coffeescript = coffeescript || require('coffee-script');
factories[topId] = coffeescript.makeNarwhalFactory(path);
return factories[topId];
coffeescript = coffeescript || require('./coffee-script');
return (factories[topId] = coffeescript.makeNarwhalFactory(path));
},
// Ensure that the coffee-script environment is loaded.
load: function(topId, path) {

View File

@@ -1 +1 @@
require("coffee-script").run(system.args)
require("./coffee-script").run(system.args)

View File

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

View File

@@ -77,36 +77,42 @@ module CoffeeScript
# If this is the top-level Expressions, wrap everything in a safety closure.
def root_compile(o={})
indent = o[:no_wrap] ? '' : TAB
code = compile(o.merge(:indent => indent, :scope => Scope.new))
code = compile(o.merge(:indent => indent, :scope => Scope.new), o[:no_wrap] ? nil : :code)
code.gsub!(STRIP_TRAILING_WHITESPACE, '')
o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
end
# The extra fancy is to handle pushing down returns and assignments
# recursively to the final lines of inner statements.
def compile(options={})
# Variables first defined within the Expressions body have their
# declarations pushed up to the top scope.
def compile(options={}, parent=nil)
return root_compile(options) unless options[:scope]
code = @expressions.map { |node|
compiled = @expressions.map do |node|
o = super(options)
if last?(node) && (o[:return] || o[:assign])
if o[:return]
if node.statement? || node.custom_return?
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
else
o.delete(:return)
"#{o[:indent]}return #{node.compile(o)}#{node.line_ending}"
end
elsif o[:assign]
if node.statement? || node.custom_assign?
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
else
"#{o[:indent]}#{AssignNode.new(ValueNode.new(LiteralNode.new(o[:assign])), node).compile(o)};"
"#{o[:indent]}#{AssignNode.new(o[:assign], node).compile(o)};"
end
end
else
o.delete(:return) and o.delete(:assign)
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
end
}.join("\n")
end
scope = options[:scope]
declarations = scope.any_declared? && parent == :code ? "#{options[:indent]}var #{scope.declared_variables.join(', ')};\n" : ''
code = declarations + compiled.join("\n")
write(code)
end
end
@@ -202,7 +208,7 @@ module CoffeeScript
def compile(o={})
o = super(o)
args = @arguments.map{|a| a.compile(o.merge(:no_paren => true)) }.join(', ')
args = @arguments.map{|a| a.compile(o) }.join(', ')
return write(compile_super(args, o)) if super?
prefix = @new ? "new " : ''
write("#{prefix}#{@variable.compile(o)}(#{args})")
@@ -300,28 +306,60 @@ module CoffeeScript
end
end
# A range literal. Ranges can be used to extract portions (slices) of arrays,
# or to specify a range for array comprehensions.
class RangeNode
attr_reader :from, :to
def initialize(from, to, exclusive=false)
@from, @to, @exclusive = from, to, exclusive
end
def exclusive?
@exclusive
end
def less_operator
@exclusive ? '<' : '<='
end
def greater_operator
@exclusive ? '>' : '>='
end
def compile(o, fv, tv)
fvv, tvv = @from.compile(o), @to.compile(o)
vars = "#{fv}=#{fvv}, #{tv}=#{tvv}"
compare = "(#{fvv} <= #{tvv} ? #{fv} #{less_operator} #{tv} : #{fv} #{greater_operator} #{tv})"
incr = "(#{fvv} <= #{tvv} ? #{fv} += 1 : #{fv} -= 1)"
"#{vars}; #{compare}; #{incr}"
end
end
# An array slice literal. Unlike JavaScript's Array#slice, the second parameter
# specifies the index of the end of the slice (just like the first parameter)
# is the index of the beginning.
class SliceNode < Node
attr_reader :from, :to
attr_reader :range
def initialize(from, to)
@from, @to = from, to
def initialize(range)
@range = range
end
def compile(o={})
o = super(o)
write(".slice(#{@from.compile(o)}, #{@to.compile(o)} + 1)")
o = super(o)
from = @range.from.compile(o)
to = @range.to.compile(o)
plus_part = @range.exclusive? ? '' : ' + 1'
write(".slice(#{from}, #{to}#{plus_part})")
end
end
# Setting the value of a local variable, or the value of an object property.
class AssignNode < Node
LEADING_VAR = /\Avar\s+/
PROTO_ASSIGN = /\A(\S+)\.prototype/
statement
custom_return
attr_reader :variable, :value, :context
@@ -336,19 +374,15 @@ module CoffeeScript
def compile(o={})
o = super(o)
name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable.to_s
last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s
name = @variable.compile(o)
last = @variable.last.to_s
proto = name[PROTO_ASSIGN, 1]
o = o.merge(:assign => name, :last_assign => last, :proto_assign => proto)
postfix = o[:return] ? ";\n#{o[:indent]}return #{name}" : ''
return write("#{@variable}: #{@value.compile(o)}") if @context == :object
return write("#{name} = #{@value.compile(o)}#{postfix}") if @variable.properties? && !@value.custom_assign?
defined = o[:scope].find(name)
def_part = defined || @variable.properties? || o[:no_wrap] ? "" : "var #{name};\n#{o[:indent]}"
return write(def_part + @value.compile(o)) if @value.custom_assign?
def_part = defined || o[:no_wrap] ? name : "var #{name}"
val_part = @value.compile(o).sub(LEADING_VAR, '')
write("#{def_part} = #{val_part}#{postfix}")
o = o.merge(:assign => @variable, :last_assign => last, :proto_assign => proto)
return write("#{name}: #{@value.compile(o)}") if @context == :object
o[:scope].find(name) unless @variable.properties?
return write(@value.compile(o)) if @value.custom_assign?
val = "#{name} = #{@value.compile(o)}"
write(o[:return] && !@value.custom_return? ? "return (#{val})" : val)
end
end
@@ -416,8 +450,8 @@ module CoffeeScript
o[:indent] += TAB
o.delete(:assign)
o.delete(:no_wrap)
@params.each {|id| o[:scope].find(id.to_s) }
code = @body.compile(o)
@params.each {|id| o[:scope].parameter(id.to_s) }
code = @body.compile(o, :code)
write("function(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
end
end
@@ -480,7 +514,7 @@ module CoffeeScript
o = super(o)
o.delete(:return)
indent = o[:indent] + TAB
cond = @condition.compile(o.merge(:no_paren => true))
cond = @condition.compile(o)
write("while (#{cond}) {\n#{@body.compile(o.merge(:indent => indent))}\n#{o[:indent]}}")
end
end
@@ -506,27 +540,36 @@ module CoffeeScript
def compile(o={})
o = super(o)
range = @source.is_a?(RangeNode)
scope = o[:scope]
name_found = scope.find(@name)
index_found = @index && scope.find(@index)
svar = scope.free_variable
ivar = scope.free_variable
ivar = range ? name : scope.free_variable
lvar = scope.free_variable
rvar = scope.free_variable
name_part = name_found ? @name : "var #{@name}"
index_name = @index ? (index_found ? @index : "var #{@index}") : nil
source_part = "var #{svar} = #{@source.compile(o)};"
for_part = "var #{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
var_part = "\n#{o[:indent] + TAB}#{name_part} = #{svar}[#{ivar}];\n"
index_part = @index ? "#{o[:indent] + TAB}#{index_name} = #{ivar};\n" : ''
index_name = @index ? @index : nil
if range
source_part = ''
var_part = ''
index_part = ''
index_var = scope.free_variable
for_part = "#{index_var}=0, #{@source.compile(o, ivar, lvar)}, #{index_var}++"
else
index_var = nil
source_part = "#{svar} = #{@source.compile(o)};\n#{o[:indent]}"
for_part = "#{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
var_part = "\n#{o[:indent] + TAB}#{@name} = #{svar}[#{ivar}];"
index_part = @index ? "\n#{o[:indent] + TAB}#{index_name} = #{ivar};" : ''
end
body = @body
suffix = ';'
set_result = "var #{rvar} = [];\n#{o[:indent]}"
save_result = "#{rvar}[#{ivar}] = "
set_result = "#{rvar} = [];\n#{o[:indent]}"
save_result = "#{rvar}[#{index_var || ivar}] = "
return_result = rvar
if o[:return] || o[:assign]
return_result = "#{o[:assign]} = #{return_result}" if o[:assign]
return_result = "#{o[:assign].compile(o)} = #{return_result}" if o[:assign]
return_result = "return #{return_result}" if o[:return]
if @filter
body = CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [@body])
@@ -541,7 +584,7 @@ module CoffeeScript
return_result = "\n#{o[:indent]}#{return_result};"
indent = o[:indent] + TAB
body = body.compile(o.merge(:indent => indent))
write("#{source_part}\n#{o[:indent]}#{set_result}for (#{for_part}) {#{var_part}#{index_part}#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}")
write("#{source_part}#{set_result}for (#{for_part}) {#{var_part}#{index_part}\n#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}")
end
end
@@ -588,7 +631,9 @@ module CoffeeScript
end
end
# An extra set of parenthesis, supplied by the script source.
# An extra set of parentheses, supplied by the script source.
# You can't wrap parentheses around bits that get compiled into JS statements,
# unfortunately.
class ParentheticalNode < Node
attr_reader :expressions
@@ -597,7 +642,7 @@ module CoffeeScript
end
def statement?
@expressions.statement?
@expressions.unwrap.statement?
end
def custom_assign?
@@ -609,10 +654,11 @@ module CoffeeScript
end
def compile(o={})
raise SyntaxError, "parentheses can't be wrapped around a statement" if statement?
o = super(o)
compiled = @expressions.compile(o)
compiled = compiled[0...-1] if compiled[-1..-1] == ';'
write(o[:no_paren] || statement? ? compiled : "(#{compiled})")
write("(#{compiled})")
end
end
@@ -682,8 +728,11 @@ module CoffeeScript
# force sub-else bodies into statement form.
def compile_statement(o)
indent = o[:indent]
cond_o = o.dup
cond_o.delete(:assign)
cond_o.delete(:return)
o[:indent] += TAB
if_part = "if (#{@condition.compile(o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
if_part = "if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
return if_part unless @else_body
else_part = chain? ?
" else #{@else_body.compile(o.merge(:indent => indent))}" :

View File

@@ -9,9 +9,9 @@ module CoffeeScript
@token_id, @value, @stack = token_id, value, stack
end
def message(source_file=nil)
def message
line = @value.respond_to?(:line) ? @value.line : "END"
line_part = source_file ? "#{source_file}:#{line}:" : "line #{line}:"
line_part = "line #{line}:"
id_part = @token_id != @value.inspect ? ", unexpected #{@token_id.downcase}" : ""
"#{line_part} syntax error for '#{@value.to_s}'#{id_part}"
end

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ module CoffeeScript
# whether a variable has been seen before or if it needs to be declared.
class Scope
attr_reader :parent, :temp_variable
attr_reader :parent, :variables, :temp_variable
# Initialize a scope with its parent, for lookups up the chain.
def initialize(parent=nil)
@@ -18,10 +18,16 @@ module CoffeeScript
def find(name, remote=false)
found = check(name, remote)
return found if found || remote
@variables[name.to_sym] = true
@variables[name.to_sym] = :var
found
end
# Define a local variable as originating from a parameter in current scope
# -- no var required.
def parameter(name)
@variables[name.to_sym] = :param
end
# Just check to see if a variable has already been declared.
def check(name, remote=false)
return true if @variables[name.to_sym]
@@ -36,10 +42,19 @@ module CoffeeScript
# Find an available, short, name for a compiler-generated variable.
def free_variable
@temp_variable.succ! while check(@temp_variable)
@variables[@temp_variable.to_sym] = true
@variables[@temp_variable.to_sym] = :var
@temp_variable.dup
end
def any_declared?
!declared_variables.empty?
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
end
end

View File

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

View File

@@ -22,6 +22,6 @@ func: =>
c.list: l for l in d.text.split('') if l is '-'.
c.single: c.list[1, 1][0].
c.single: c.list[1..1][0].
print(func() == '-')

View File

@@ -0,0 +1,3 @@
num: 1 + 2 + (a: 3)
print(num is 6)

View File

@@ -0,0 +1,6 @@
six: \
1 + \
2 + \
3
print(six is 6)

View File

@@ -0,0 +1,8 @@
nums: i * 3 for i in [1..3].
negs: x for x in [-20..-10].
negs: negs[0..2]
result: nums.concat(negs).join(', ')
print(result is '3, 6, 9, -20, -19, -18')

View File

@@ -0,0 +1,8 @@
array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a: array[7..9]
b: array[2...4]
result: a.concat(b).join(' ')
print(result is "7 8 9 2 3")

View File

@@ -3,24 +3,25 @@
// The cornerstone, an each implementation.
// Handles objects implementing forEach, arrays, and raw objects.
_.each = function(obj, iterator, context) {
var index = 0;
var __a, __b, __c, __d, __e, __f, __g, __h, i, index, item, key;
index = 0;
try {
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (_.isArray(obj) || _.isArguments(obj)) {
var __a = obj;
var __d = [];
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var item = __a[__b];
var i = __b;
__a = obj;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
item = __a[__b];
i = __b;
__d[__b] = iterator.call(context, item, i, obj);
}
__d;
} else {
var __e = _.keys(obj);
var __h = [];
for (var __f=0, __g=__e.length; __f<__g; __f++) {
var key = __e[__f];
__e = _.keys(obj);
__h = [];
for (__f=0, __g=__e.length; __f<__g; __f++) {
key = __e[__f];
__h[__f] = iterator.call(context, obj[key], key, obj);
}
__h;

View File

@@ -2,24 +2,25 @@
// The cornerstone, an each implementation.
// Handles objects implementing forEach, arrays, and raw objects.
_.each = function(obj, iterator, context) {
var index = 0;
var __a, __b, __c, __d, __e, __f, __g, __h, i, index, item, key;
index = 0;
try {
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (_.isArray(obj) || _.isArguments(obj)) {
var __a = obj;
var __d = [];
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var item = __a[__b];
var i = __b;
__a = obj;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
item = __a[__b];
i = __b;
__d[__b] = iterator.call(context, item, i, obj);
}
__d;
} else {
var __e = _.keys(obj);
var __h = [];
for (var __f=0, __g=__e.length; __f<__g; __f++) {
var key = __e[__f];
__e = _.keys(obj);
__h = [];
for (__f=0, __g=__e.length; __f<__g; __f++) {
key = __e[__f];
__h[__f] = iterator.call(context, obj[key], key, obj);
}
__h;

View File

@@ -1,12 +1,13 @@
(function(){
var object = {
var array, object;
object = {
a: 1,
// Comments between the elements.
b: 2,
// Like this.
c: 3
};
var array = [1,
array = [1,
// Comments between the elements.
2,
// Like this.

View File

@@ -3,20 +3,25 @@ require 'test_helper'
class ExecutionTest < Test::Unit::TestCase
NO_WARNINGS = /\A(0 error\(s\), 0 warning\(s\)\n)+\Z/
ALLS_WELL = /\A\n?(true\n)+\Z/
ALLS_WELL = /\A\n?(true\n)+\Z/m
def test_execution_of_coffeescript
sources = ['test/fixtures/execution/*.coffee'].join(' ')
assert `bin/coffee-script -r #{sources}`.match(ALLS_WELL)
assert `bin/coffee -r #{sources}`.match(ALLS_WELL)
end
def test_lintless_coffeescript
lint_results = `bin/coffee-script -l test/fixtures/execution/*.coffee`
lint_results = `bin/coffee -l test/fixtures/execution/*.coffee`
assert lint_results.match(NO_WARNINGS)
end
def test_lintless_examples
lint_results = `bin/coffee-script -l examples/*.coffee`
lint_results = `bin/coffee -l examples/*.coffee`
assert lint_results.match(NO_WARNINGS)
end
def test_lintless_documentation
lint_results = `bin/coffee -l documentation/coffee/*.coffee`
assert lint_results.match(NO_WARNINGS)
end

View File

@@ -38,13 +38,19 @@ class LexerTest < Test::Unit::TestCase
def test_lexing_comment
code = "a: 1\n # comment\n # on two lines\nb: 2"
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"], [:NUMBER, "1"],
["\n", "\n"], [:COMMENT, [" comment", " on two lines"]], ["\n", "\n"],
[:IDENTIFIER, "b"], [:ASSIGN, ":"], [:NUMBER, "2"]]
["\n", "\n"], [:COMMENT, [" comment", " on two lines"]], ["\n", "\n"],
[:IDENTIFIER, "b"], [:ASSIGN, ":"], [:NUMBER, "2"]]
end
def test_lexing_newline_escaper
code = "two: 1 + \\\n\n 1"
assert @lex.tokenize(code) == [[:IDENTIFIER, "two"], [:ASSIGN, ":"],
[:NUMBER, "1"], ["+", "+"], [:NUMBER, "1"]]
end
def test_lexing
tokens = @lex.tokenize(File.read('test/fixtures/each.coffee'))
assert tokens.inspect == File.read('test/fixtures/each.tokens')
tokens = @lex.tokenize(File.read('test/fixtures/generation/each.coffee'))
assert tokens.inspect == File.read('test/fixtures/generation/each.tokens')
end
end

View File

@@ -24,8 +24,8 @@ class ParserTest < Test::Unit::TestCase
nodes = @par.parse("{one : 1 \n two : 2}").expressions
obj = nodes.first.literal
assert obj.is_a? ObjectNode
assert obj.properties.first.variable == "one"
assert obj.properties.last.variable == "two"
assert obj.properties.first.variable.literal.value == "one"
assert obj.properties.last.variable.literal.value == "two"
end
def test_parsing_an_function_definition
@@ -49,7 +49,7 @@ class ParserTest < Test::Unit::TestCase
assert nodes.first.is_a? ForNode
assert nodes.first.body.literal == 'i'
assert nodes.first.filter.operator == '==='
assert nodes.first.source.literal.objects.last.value == "5"
assert nodes.first.source.literal.objects.last.literal.value == "5"
end
def test_parsing_comment
@@ -58,23 +58,29 @@ class ParserTest < Test::Unit::TestCase
end
def test_parsing_inner_comments
nodes = @par.parse(File.read('test/fixtures/inner_comments.coffee'))
assert nodes.compile == File.read('test/fixtures/inner_comments.js')
nodes = @par.parse(File.read('test/fixtures/generation/inner_comments.coffee'))
assert nodes.compile == File.read('test/fixtures/generation/inner_comments.js')
end
def test_parsing
nodes = @par.parse(File.read('test/fixtures/each.coffee'))
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.value.is_a? CodeNode
assert assign.value.params == ['obj', 'iterator', 'context']
assert nodes.compile == File.read('test/fixtures/each.js')
assert nodes.compile == File.read('test/fixtures/generation/each.js')
end
def test_no_wrap
nodes = @par.parse(File.read('test/fixtures/each.coffee'))
assert nodes.compile(:no_wrap => true) == File.read('test/fixtures/each_no_wrap.js')
nodes = @par.parse(File.read('test/fixtures/generation/each.coffee'))
assert nodes.compile(:no_wrap => true) == File.read('test/fixtures/generation/each_no_wrap.js')
end
def test_no_wrapping_parens_around_statements
assert_raises(SyntaxError) do
@par.parse("(try thing() catch error fail().)").compile
end
end
end