Compare commits

..

30 Commits
0.1.0 ... 0.1.3

Author SHA1 Message Date
Jeremy Ashkenas
859ab7583f bumping to 0.1.3 ... here we go 2009-12-25 00:16:56 -08:00
Jeremy Ashkenas
3eedd5bb50 better error warnings on the command line 2009-12-25 00:02:27 -08:00
Jeremy Ashkenas
5b7e695f6c removed bin/cs in favor of a more comprehensive coffee-script command ... now with --interactive and --run 2009-12-24 23:57:27 -08:00
Jeremy Ashkenas
1e3182727b majorly cleaned up the CoffeeScript that defines the Narwhal integration 2009-12-24 23:28:01 -08:00
Jeremy Ashkenas
e595dbfcee the narwhal integration written in JavaScript has been replaced with CoffeeScript, and compiler-generated variable names now start with '__' 2009-12-24 23:09:24 -08:00
Jeremy Ashkenas
12b830893d sped up the execution test a good deal by running it all in one pass 2009-12-24 22:29:30 -08:00
Jeremy Ashkenas
cca80342aa making all assignment-y operators use a colon -- now it's +: -: *: /:, and friends 2009-12-24 22:25:29 -08:00
Jeremy Ashkenas
4412f590cf removed dependency on v8 in favor of bin/cs 2009-12-24 22:08:32 -08:00
tlrobinson
0cd2d78027 Print compiler errors to stderr 2009-12-24 19:34:17 -08:00
Jeremy Ashkenas
be672ebfc1 fixed the bin/cs repl to save assignment between commands by using the new --no-wrap 2009-12-24 17:45:23 -08:00
Jeremy Ashkenas
9047c87567 the --no-wrap option now disables top-level var declarations 2009-12-24 17:37:24 -08:00
Jeremy Ashkenas
31d630bb91 updating docs for isnt 2009-12-24 17:22:46 -08:00
Jeremy Ashkenas
7a2f5a333f trading aint for isnt -- let's be serious 2009-12-24 17:21:20 -08:00
Jeremy Ashkenas
66303636dc allowing quoted strings within object assignment, a in JS and JSON 2009-12-24 17:14:53 -08:00
Jeremy Ashkenas
9dc932e380 bumping to 0.1.2 to get the super()/extends fix out there 2009-12-24 17:05:55 -08:00
Jeremy Ashkenas
a71de4b5b6 got extends back in the language -- use it together with super 2009-12-24 16:49:23 -08:00
Jeremy Ashkenas
ada8dfc6d4 fixing super() calls, thanks to tolmasky 2009-12-24 16:23:23 -08:00
Jeremy Ashkenas
4112595368 removing the special-case std-reading in favor of '--eval' 2009-12-24 15:49:42 -08:00
Jeremy Ashkenas
c281db7730 document that -e can read from stdin 2009-12-24 15:35:58 -08:00
Jeremy Ashkenas
5e6b49ad1e with a working -n --no-wrap option to disable the top-level function safety wrapper 2009-12-24 15:31:00 -08:00
Jeremy Ashkenas
ec1a527575 Merge branch 'master' of git://github.com/tlrobinson/coffee-script 2009-12-24 15:05:56 -08:00
tlrobinson
dc0ab1afca Command line CoffeeScript 2009-12-24 14:42:57 -08:00
tlrobinson
ae72fbfd0d Narwhal support for CoffeeScript 2009-12-24 14:41:35 -08:00
tlrobinson
ad0735f765 Read from stdin if source is "-" 2009-12-24 14:40:39 -08:00
Jeremy Ashkenas
d49c178f1d added and -> &&, or -> || to the docs (they were missing) 2009-12-24 14:37:30 -08:00
Jeremy Ashkenas
8f8ba255b3 docs for 0.1.1 2009-12-24 12:02:28 -08:00
Jeremy Ashkenas
a4bc24817d bumping to 0.1.1 2009-12-24 11:59:19 -08:00
Jeremy Ashkenas
9eeac9b272 added the typeof operater as an OpNode 2009-12-24 11:50:44 -08:00
Jeremy Ashkenas
f859eb6cec added the instanceof operator to the grammar as an operation node 2009-12-24 11:46:51 -08:00
Jeremy Ashkenas
b29afc2c09 another wish 2009-12-24 10:31:44 -08:00
42 changed files with 1781 additions and 1318 deletions

View File

@@ -10,9 +10,18 @@ task :test do
Dir['test/*/**/test_*.rb'].each {|test| require test }
end
desc "Recompile the Racc parser (pass -v and -g for verbose debugging)"
task :build, :extra_args do |t, args|
sh "racc #{args[:extra_args]} -o lib/coffee_script/parser.rb lib/coffee_script/grammar.y"
namespace :build do
desc "Recompile the Racc parser (pass -v and -g for verbose debugging)"
task :parser, :extra_args do |t, args|
sh "racc #{args[:extra_args]} -o lib/coffee_script/parser.rb lib/coffee_script/grammar.y"
end
desc "Compile the Narwhal interface for --interactive and --run"
task :narwhal do
sh "bin/coffee-script lib/coffee_script/narwhal/*.cs -o lib/coffee_script/narwhal/js"
end
end
desc "Build the documentation page"

View File

@@ -1,12 +1,17 @@
Gem::Specification.new do |s|
s.name = 'coffee-script'
s.version = '0.1.0' # Keep version in sync with coffee-script.rb
s.date = '2009-12-24'
s.version = '0.1.3' # Keep version in sync with coffee-script.rb
s.date = '2009-12-25'
s.homepage = "http://jashkenas.github.com/coffee-script/"
s.summary = "The CoffeeScript Compiler"
s.description = <<-EOS
CoffeeScript is a little language that compiles into JavaScript.
CoffeeScript is a little language that compiles into JavaScript. Think
of it as JavaScript's less ostentatious kid brother -- the same genes,
roughly the same height, but a different sense of style. Apart from a
handful of bonus goodies, statements in CoffeeScript correspond
one-to-one with their equivalent in JavaScript, it's just another
way of saying it.
EOS
s.authors = ['Jeremy Ashkenas']

View File

@@ -1,6 +1,6 @@
launch() if ignition is on
volume: 10 if band aint spinal_tap
volume: 10 if band isnt spinal_tap
let_the_wild_rumpus_begin() unless answer is no

View File

@@ -3,13 +3,13 @@ Animal.prototype.move: meters =>
alert(this.name + " moved " + meters + "m.").
Snake: name => this.name: name.
Snake.prototype: new Animal()
Snake extends new Animal()
Snake.prototype.move: =>
alert("Slithering...")
super(5).
Horse: name => this.name: name.
Horse.prototype: new Animal()
Horse extends new Animal()
Horse.prototype.move: =>
alert("Galloping...")
super(45).

View File

@@ -37,8 +37,6 @@
equivalent in JavaScript, it's just another way of saying it.
</p>
<!-- <%# code_for('intro') %>-->
<p>
<b>Disclaimer:</b>
CoffeeScript is just for fun and seriously alpha. I'm sure that there are still
@@ -67,7 +65,7 @@
<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="#super">Calling Super from a Subclass</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 />
@@ -83,7 +81,7 @@
<%= code_for('overview', 'cubed_list') %>
<h2 id="installation">Installation and Usage</h2>
<p>
The CoffeeScript compiler is written in pure Ruby, and is available
as a Ruby Gem.
@@ -95,14 +93,31 @@ gem install coffee-script</pre>
<p>
Installing the gem provides the <tt>coffee-script</tt> command, which can
be used to compile CoffeeScript <tt>.cs</tt> files into JavaScript, as
well as debug them. By default, <tt>coffee-script</tt> writes out the
JavaScript as <tt>.js</tt> files in the same directory, but output
well as debug them. In conjunction with
<a href="http://narwhaljs.org/">Narwhal</a>, the <tt>coffee-script</tt>
command also provides direct evaluation and an interactive REPL.
When compiling to JavaScript, <tt>coffee-script</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>
<table>
<tr>
<td width="25%"><code>-o, --output [DIR]</code></td>
<td width="25%"><code>-i, --interactive</code></td>
<td>
Launch an interactive CoffeeScript session.
Requires <a href="http://narwhaljs.org/">Narwhal</a>.
</td>
</tr>
<tr>
<td><code>-r, --run</code></td>
<td>
Compile and execute the CoffeeScripts without saving the intermediate
JavaScript. Requires <a href="http://narwhaljs.org/">Narwhal</a>.
</td>
</tr>
<tr>
<td><code>-o, --output [DIR]</code></td>
<td>
Write out all compiled JavaScript files into the specified directory.
</td>
@@ -125,7 +140,7 @@ gem install coffee-script</pre>
<td><code>-l, --lint</code></td>
<td>
If the <tt>jsl</tt> (JavaScript Lint) command is installed, use it
to check the compilation of a CoffeeScript file. (Handy in
to check the compilation of a CoffeeScript file. (Handy in
conjunction with <tt>--watch</tt>)
</td>
</tr>
@@ -151,6 +166,14 @@ gem install coffee-script</pre>
AST.
</td>
</tr>
<tr>
<td><code>-n, --no-wrap</code></td>
<td>
Compile the JavaScript without the top-level function safety wrapper
or var declarations, for situations where you want to add every
variable to global scope.
</td>
</tr>
<tr>
<td><code>--install-bundle</code></td>
<td>
@@ -186,15 +209,15 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
the line will do just as well. All other whitespace is
not significant. Instead of using curly braces <tt>{ }</tt>
to delimit a block of code, use a period <tt>.</tt> to mark the end of a
block, for
<a href="#functions">functions</a>,
<a href="#conditionals">if-statements</a>,
block, for
<a href="#functions">functions</a>,
<a href="#conditionals">if-statements</a>,
<a href="#switch">switch</a>, and <a href="#try">try/catch</a>.
</p>
<p id="functions">
<b class="header">Functions and Invocation</b>
Functions are defined by a list of parameters, an arrow, and the
Functions are defined by a list of parameters, an arrow, and the
function body. The empty function looks like this: <tt>=>.</tt>
</p>
<%= code_for('functions', 'cube(5)') %>
@@ -270,7 +293,7 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
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).
</p>
<p id="aliases">
<b class="header">Aliases</b>
Because the <tt>==</tt> operator frequently causes undesirable coercion,
@@ -278,14 +301,18 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
CoffeeScript compiles <tt>==</tt> into <tt>===</tt>, and <tt>!=</tt> into
<tt>!==</tt>.
In addition, <tt>is</tt> compiles into <tt>===</tt>,
and <tt>aint</tt> into <tt>!==</tt>.
and <tt>isnt</tt> into <tt>!==</tt>.
</p>
<p>
You can use <tt>not</tt> as an alias for <tt>!</tt>.
</p>
<p>
For logic, <tt>and</tt> compiles to <tt>&amp;&amp;</tt>, and <tt>or</tt>
into <tt>||</tt>.
</p>
<p>
Instead of a newline or semicolon, <tt>then</tt> can be used to separate
conditions from expressions, in <b>while</b>,
conditions from expressions, in <b>while</b>,
<b>if</b>/<b>else</b>, and <b>switch</b>/<b>when</b> statements.
</p>
<p>
@@ -328,8 +355,8 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
</p>
<%= code_for('slices', 'three_to_six') %>
<p id="super">
<b class="header">Calling Super from a Subclass</b>
<p id="inheritance">
<b class="header">Inheritance, and Calling Super from a Subclass</b>
JavaScript's prototypal inheritance has always been a bit of a
brain-bender, with a whole family tree of libraries that provide a cleaner
syntax for classical inheritance on top of JavaScript's prototypes:
@@ -337,9 +364,11 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
<a href="http://prototypejs.org/">Prototype.js</a>,
<a href="http://jsclass.jcoglan.com/">JS.Class</a>, etc.
The libraries provide syntactic sugar, but the built-in inheritance would
be completely usable if it weren't for one small exception:
it's very awkward to call <b>super</b>, the prototype object's
implementation of the current function. CoffeeScript converts
be completely usable if it weren't for a couple of small exceptions:
it's awkward to call <b>super</b> (the prototype object's
implementation of the current function), and it's awkward to correctly
set the prototype chain. CoffeeScript provides <tt>extends</tt>
to help with prototype setup, and converts
<tt>super()</tt> calls into calls against the immediate ancestor's
method of the same name.
</p>
@@ -376,14 +405,14 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
Multiline strings are allowed in CoffeeScript.
</p>
<%= code_for('strings', 'moby_dick') %>
<h2 id="contributing">Contributing</h2>
<p>
Here's a wish list of things that would be wonderful to have in
CoffeeScript:
</p>
<ul>
<li>
A JavaScript version of the compiler, perhaps using Alessandro Warth's
@@ -398,19 +427,55 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
should be able to compile properly.
</li>
<li>
A tutorial that introduces CoffeeScript from the ground up for folks
A tutorial that introduces CoffeeScript from the ground up for folks
without knowledge of JavaScript.
</li>
<li>
Integration with Processing.js's JavaScript API (this would depend on
having a JavaScript version of the compiler).
</li>
<li>
A lot of the code generation in <tt>nodes.rb</tt> gets into messy
string manipulation. Techniques for cleaning this up across the board
would be appreciated.
</li>
</ul>
<h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 25px;">0.1.0</b>
<b class="header" style="margin-top: 20px;">0.1.3</b>
The <tt>coffee-script</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.
The <tt>aint</tt> keyword has been replaced by <tt>isnt</tt>, which goes
together a little smoother with <tt>is</tt>.
Quoted strings are now allowed as identifiers within object literals: eg.
<tt>{"5+5": 10}</tt>.
All assignment operators now use a colon: <tt>+:</tt>, <tt>-:</tt>,
<tt>*:</tt>, etc.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.2</b>
Fixed a bug with calling <tt>super()</tt> through more than one level of
inheritance, with the re-addition of the <tt>extends</tt> keyword.
Added experimental <a href="http://narwhaljs.org/">Narwhal</a>
support (as a Tusk package), contributed by
<a href="http://tlrobinson.net/">Tom Robinson</a>, including
<b>bin/cs</b> as a CoffeeScript REPL and interpreter.
New <tt>--no-wrap</tt> option to suppress the safety function
wrapper.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.1</b>
Added <tt>instanceof</tt> and <tt>typeof</tt> as operators.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.0</b>
Initial CoffeeScript release.
</p>

View File

@@ -2,18 +2,18 @@
// 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];
d[b] = food.eat();
var __a = ['toast', 'cheese', 'wine'];
var __d = [];
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var food = __a[__b];
__d[__b] = food.eat();
}
lunch = d;
lunch = __d;
// Zebra-stripe a table.
var e = table;
for (var f=0, g=e.length; f<g; f++) {
var row = e[f];
var i = f;
var __e = table;
for (var __f=0, __g=__e.length; __f<__g; __f++) {
var row = __e[__f];
var i = __f;
i % 2 === 0 ? highlight(row) : null;
}
})();

View File

@@ -23,11 +23,11 @@
};
// Array comprehensions:
var cubed_list;
var a = list;
var d = [];
for (var b=0, c=a.length; b<c; b++) {
var num = a[b];
d[b] = math.cube(num);
var __a = list;
var __d = [];
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var num = __a[__b];
__d[__b] = math.cube(num);
}
cubed_list = d;
cubed_list = __d;
})();

View File

@@ -8,19 +8,19 @@
this.name = name;
return this.name;
};
Snake.prototype = new Animal();
Snake.prototype.__proto__ = new Animal();
Snake.prototype.move = function() {
alert("Slithering...");
return this.constructor.prototype.move.call(this, 5);
return Snake.prototype.__proto__.move.call(this, 5);
};
var Horse = function(name) {
this.name = name;
return this.name;
};
Horse.prototype = new Animal();
Horse.prototype.__proto__ = new Animal();
Horse.prototype.move = function() {
alert("Galloping...");
return this.constructor.prototype.move.call(this, 45);
return Horse.prototype.__proto__.move.call(this, 45);
};
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");

View File

@@ -5,7 +5,7 @@ sum: x, y => x + y.
odd: x => x % 2 is 0.
even: x => x % 2 aint 0.
even: x => x % 2 isnt 0.
run_loop: =>
fire_events( e => e.stopPropagation(). )
@@ -145,13 +145,13 @@ Animal.prototype.move: meters =>
alert(this.name + " moved " + meters + "m.").
Snake: name => this.name: name.
Snake.prototype: Animal
Snake extends new Animal()
Snake.prototype.move: =>
alert('Slithering...')
super(5).
Horse: name => this.name: name.
Horse.prototype: Animal
Horse extends new Animal()
Horse.prototype.move: =>
alert('Galloping...')
super(45).

View File

@@ -81,9 +81,9 @@ Creature : {
hit: damage =>
p_up: Math.rand( this.charisma )
if p_up % 9 is 7
this.life += p_up / 4
this.life +: p_up / 4
puts( "[" + this.name + " magick powers up " + p_up + "!]" ).
this.life -= damage
this.life -: damage
if this.life <= 0 then puts( "[" + this.name + " has died.]" )..
# This method takes one turn in a fight.

View File

@@ -49,7 +49,7 @@ _.each: obj, iterator, context =>
return iterator.call(context, item, i, obj) for item, i in obj. if _.isArray(obj) or _.isArguments(obj)
iterator.call(context, obj[key], key, obj) for key in _.keys(obj).
catch e
throw e if e aint breaker.
throw e if e isnt breaker.
obj.
# Return the results of applying the iterator to each element. Use JavaScript

View File

@@ -23,8 +23,6 @@
equivalent in JavaScript, it's just another way of saying it.
</p>
<!-- -->
<p>
<b>Disclaimer:</b>
CoffeeScript is just for fun and seriously alpha. I'm sure that there are still
@@ -53,7 +51,7 @@
<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="#super">Calling Super from a Subclass</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 />
@@ -112,13 +110,13 @@ 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];
d[b] <span class="Keyword">=</span> math.cube(num);
<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];
__d[__b] <span class="Keyword">=</span> math.cube(num);
}
cubed_list <span class="Keyword">=</span> d;
cubed_list <span class="Keyword">=</span> __d;
</pre><button onclick='javascript:
// Assignment:
var number = 42;
@@ -143,17 +141,17 @@ 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];
d[b] = math.cube(num);
var __a = list;
var __d = [];
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var num = __a[__b];
__d[__b] = math.cube(num);
}
cubed_list = d;
cubed_list = __d;
;alert(cubed_list);'>run: cubed_list</button><br class='clear' /></div>
<h2 id="installation">Installation and Usage</h2>
<p>
The CoffeeScript compiler is written in pure Ruby, and is available
as a Ruby Gem.
@@ -165,14 +163,31 @@ gem install coffee-script</pre>
<p>
Installing the gem provides the <tt>coffee-script</tt> command, which can
be used to compile CoffeeScript <tt>.cs</tt> files into JavaScript, as
well as debug them. By default, <tt>coffee-script</tt> writes out the
JavaScript as <tt>.js</tt> files in the same directory, but output
well as debug them. In conjunction with
<a href="http://narwhaljs.org/">Narwhal</a>, the <tt>coffee-script</tt>
command also provides direct evaluation and an interactive REPL.
When compiling to JavaScript, <tt>coffee-script</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>
<table>
<tr>
<td width="25%"><code>-o, --output [DIR]</code></td>
<td width="25%"><code>-i, --interactive</code></td>
<td>
Launch an interactive CoffeeScript session.
Requires <a href="http://narwhaljs.org/">Narwhal</a>.
</td>
</tr>
<tr>
<td><code>-r, --run</code></td>
<td>
Compile and execute the CoffeeScripts without saving the intermediate
JavaScript. Requires <a href="http://narwhaljs.org/">Narwhal</a>.
</td>
</tr>
<tr>
<td><code>-o, --output [DIR]</code></td>
<td>
Write out all compiled JavaScript files into the specified directory.
</td>
@@ -195,7 +210,7 @@ gem install coffee-script</pre>
<td><code>-l, --lint</code></td>
<td>
If the <tt>jsl</tt> (JavaScript Lint) command is installed, use it
to check the compilation of a CoffeeScript file. (Handy in
to check the compilation of a CoffeeScript file. (Handy in
conjunction with <tt>--watch</tt>)
</td>
</tr>
@@ -221,6 +236,14 @@ gem install coffee-script</pre>
AST.
</td>
</tr>
<tr>
<td><code>-n, --no-wrap</code></td>
<td>
Compile the JavaScript without the top-level function safety wrapper
or var declarations, for situations where you want to add every
variable to global scope.
</td>
</tr>
<tr>
<td><code>--install-bundle</code></td>
<td>
@@ -256,15 +279,15 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
the line will do just as well. All other whitespace is
not significant. Instead of using curly braces <tt>{ }</tt>
to delimit a block of code, use a period <tt>.</tt> to mark the end of a
block, for
<a href="#functions">functions</a>,
<a href="#conditionals">if-statements</a>,
block, for
<a href="#functions">functions</a>,
<a href="#conditionals">if-statements</a>,
<a href="#switch">switch</a>, and <a href="#try">try/catch</a>.
</p>
<p id="functions">
<b class="header">Functions and Invocation</b>
Functions are defined by a list of parameters, an arrow, and the
Functions are defined by a list of parameters, an arrow, and the
function body. The empty function looks like this: <tt>=>.</tt>
</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.
@@ -445,7 +468,7 @@ var eldest = 24 > 21 ? "Liz" : "Ike";
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).
</p>
<p id="aliases">
<b class="header">Aliases</b>
Because the <tt>==</tt> operator frequently causes undesirable coercion,
@@ -453,14 +476,18 @@ var eldest = 24 > 21 ? "Liz" : "Ike";
CoffeeScript compiles <tt>==</tt> into <tt>===</tt>, and <tt>!=</tt> into
<tt>!==</tt>.
In addition, <tt>is</tt> compiles into <tt>===</tt>,
and <tt>aint</tt> into <tt>!==</tt>.
and <tt>isnt</tt> into <tt>!==</tt>.
</p>
<p>
You can use <tt>not</tt> as an alias for <tt>!</tt>.
</p>
<p>
For logic, <tt>and</tt> compiles to <tt>&amp;&amp;</tt>, and <tt>or</tt>
into <tt>||</tt>.
</p>
<p>
Instead of a newline or semicolon, <tt>then</tt> can be used to separate
conditions from expressions, in <b>while</b>,
conditions from expressions, in <b>while</b>,
<b>if</b>/<b>else</b>, and <b>switch</b>/<b>when</b> statements.
</p>
<p>
@@ -472,7 +499,7 @@ var eldest = 24 > 21 ? "Liz" : "Ike";
</p>
<div class='code'><pre class="idle">launch() <span class="Keyword">if</span> ignition <span class="Keyword">is</span> <span class="BuiltInConstant">on</span>
volume<span class="Keyword">:</span> <span class="Number">10</span> <span class="Keyword">if</span> band <span class="Keyword">aint</span> spinal_tap
volume<span class="Keyword">:</span> <span class="Number">10</span> <span class="Keyword">if</span> band <span class="Keyword">isnt</span> spinal_tap
let_the_wild_rumpus_begin() <span class="Keyword">unless</span> answer <span class="Keyword">is</span> <span class="BuiltInConstant">no</span>
@@ -531,18 +558,18 @@ highlight(row) <span class="Keyword">for</span> row, i <span class="Keyword">in<
</pre><pre class="idle">
<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];
d[b] <span class="Keyword">=</span> food.eat();
<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];
__d[__b] <span class="Keyword">=</span> food.eat();
}
lunch <span class="Keyword">=</span> d;
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="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;
<span class="Storage">var</span> __e <span class="Keyword">=</span> table;
<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;
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>;
}
</pre><br class='clear' /></div>
@@ -561,8 +588,8 @@ three_to_six<span class="Keyword">:</span> nums[<span class="Number">3</span>, <
var three_to_six = nums.slice(3, 6 + 1);
;alert(three_to_six);'>run: three_to_six</button><br class='clear' /></div>
<p id="super">
<b class="header">Calling Super from a Subclass</b>
<p id="inheritance">
<b class="header">Inheritance, and Calling Super from a Subclass</b>
JavaScript's prototypal inheritance has always been a bit of a
brain-bender, with a whole family tree of libraries that provide a cleaner
syntax for classical inheritance on top of JavaScript's prototypes:
@@ -570,9 +597,11 @@ var three_to_six = nums.slice(3, 6 + 1);
<a href="http://prototypejs.org/">Prototype.js</a>,
<a href="http://jsclass.jcoglan.com/">JS.Class</a>, etc.
The libraries provide syntactic sugar, but the built-in inheritance would
be completely usable if it weren't for one small exception:
it's very awkward to call <b>super</b>, the prototype object's
implementation of the current function. CoffeeScript converts
be completely usable if it weren't for a couple of small exceptions:
it's awkward to call <b>super</b> (the prototype object's
implementation of the current function), and it's awkward to correctly
set the prototype chain. CoffeeScript provides <tt>extends</tt>
to help with prototype setup, and converts
<tt>super()</tt> calls into calls against the immediate ancestor's
method of the same name.
</p>
@@ -581,13 +610,13 @@ var three_to_six = nums.slice(3, 6 + 1);
alert(<span class="Variable">this</span>.name <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>).
<span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</span> <span class="Variable">this</span>.name<span class="Keyword">:</span> name.
Snake.prototype<span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Animal</span>()
Snake <span class="Variable">extends</span> <span class="Keyword">new</span> <span class="TypeName">Animal</span>()
<span class="FunctionName">Snake.prototype.move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(<span class="String"><span class="String">&quot;</span>Slithering...<span class="String">&quot;</span></span>)
<span class="Variable">super</span>(<span class="Number">5</span>).
<span class="FunctionName">Horse</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=&gt;</span> <span class="Variable">this</span>.name<span class="Keyword">:</span> name.
Horse.prototype<span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Animal</span>()
Horse <span class="Variable">extends</span> <span class="Keyword">new</span> <span class="TypeName">Animal</span>()
<span class="FunctionName">Horse.prototype.move</span><span class="Keyword">:</span> <span class="Storage">=&gt;</span>
alert(<span class="String"><span class="String">&quot;</span>Galloping...<span class="String">&quot;</span></span>)
<span class="Variable">super</span>(<span class="Number">45</span>).
@@ -610,19 +639,19 @@ tom.move()
<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="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
<span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">__proto__</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
<span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">move</span> = <span class="Storage">function</span>() {
<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> <span class="Variable">this</span>.<span class="LibraryConstant">constructor</span>.<span class="LibraryConstant">prototype</span>.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">5</span>);
<span class="Keyword">return</span> Snake.<span class="LibraryConstant">prototype</span>.__proto__.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="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
<span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">__proto__</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
<span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">move</span> = <span class="Storage">function</span>() {
<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> <span class="Variable">this</span>.<span class="LibraryConstant">constructor</span>.<span class="LibraryConstant">prototype</span>.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">45</span>);
<span class="Keyword">return</span> Horse.<span class="LibraryConstant">prototype</span>.__proto__.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>);
@@ -637,19 +666,19 @@ var Snake = function(name) {
this.name = name;
return this.name;
};
Snake.prototype = new Animal();
Snake.prototype.__proto__ = new Animal();
Snake.prototype.move = function() {
alert("Slithering...");
return this.constructor.prototype.move.call(this, 5);
return Snake.prototype.__proto__.move.call(this, 5);
};
var Horse = function(name) {
this.name = name;
return this.name;
};
Horse.prototype = new Animal();
Horse.prototype.__proto__ = new Animal();
Horse.prototype.move = function() {
alert("Galloping...");
return this.constructor.prototype.move.call(this, 45);
return Horse.prototype.__proto__.move.call(this, 45);
};
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");
@@ -755,14 +784,14 @@ 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="contributing">Contributing</h2>
<p>
Here's a wish list of things that would be wonderful to have in
CoffeeScript:
</p>
<ul>
<li>
A JavaScript version of the compiler, perhaps using Alessandro Warth's
@@ -777,19 +806,55 @@ world...";
should be able to compile properly.
</li>
<li>
A tutorial that introduces CoffeeScript from the ground up for folks
A tutorial that introduces CoffeeScript from the ground up for folks
without knowledge of JavaScript.
</li>
<li>
Integration with Processing.js's JavaScript API (this would depend on
having a JavaScript version of the compiler).
</li>
<li>
A lot of the code generation in <tt>nodes.rb</tt> gets into messy
string manipulation. Techniques for cleaning this up across the board
would be appreciated.
</li>
</ul>
<h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 25px;">0.1.0</b>
<b class="header" style="margin-top: 20px;">0.1.3</b>
The <tt>coffee-script</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.
The <tt>aint</tt> keyword has been replaced by <tt>isnt</tt>, which goes
together a little smoother with <tt>is</tt>.
Quoted strings are now allowed as identifiers within object literals: eg.
<tt>{"5+5": 10}</tt>.
All assignment operators now use a colon: <tt>+:</tt>, <tt>-:</tt>,
<tt>*:</tt>, etc.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.2</b>
Fixed a bug with calling <tt>super()</tt> through more than one level of
inheritance, with the re-addition of the <tt>extends</tt> keyword.
Added experimental <a href="http://narwhaljs.org/">Narwhal</a>
support (as a Tusk package), contributed by
<a href="http://tlrobinson.net/">Tom Robinson</a>, including
<b>bin/cs</b> as a CoffeeScript REPL and interpreter.
New <tt>--no-wrap</tt> option to suppress the safety function
wrapper.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.1</b>
Added <tt>instanceof</tt> and <tt>typeof</tt> as operators.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.0</b>
Initial CoffeeScript release.
</p>

View File

@@ -9,12 +9,12 @@ require "coffee_script/parse_error"
# Namespace for all CoffeeScript internal classes.
module CoffeeScript
VERSION = '0.1.0' # Keep in sync with the gemspec.
VERSION = '0.1.3' # Keep in sync with the gemspec.
# Compile a script (String or IO) to JavaScript.
def self.compile(script)
def self.compile(script, options={})
script = script.read if script.respond_to?(:read)
Parser.new.parse(script).compile
Parser.new.parse(script).compile(options)
end
end

View File

@@ -229,7 +229,7 @@
</dict>
<dict>
<key>match</key>
<string>\b(super|this)\b</string>
<string>\b(super|this|extends)\b</string>
<key>name</key>
<string>variable.language.cs</string>
</dict>
@@ -241,7 +241,7 @@
</dict>
<dict>
<key>match</key>
<string>!|\$|%|&amp;|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|aint|not)\b</string>
<string>!|\$|%|&amp;|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*:|(?&lt;!\()/=|%:|\+:|\-:|&amp;=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
<key>name</key>
<string>keyword.operator.cs</string>
</dict>

View File

@@ -23,8 +23,10 @@ Usage:
def initialize
@mtimes = {}
parse_options
return launch_repl if @options[:interactive]
return eval_scriptlet if @options[:eval]
check_sources
return run_scripts if @options[:run]
@sources.each {|source| compile_javascript(source) }
watch_coffee_scripts if @options[:watch]
end
@@ -100,6 +102,23 @@ Usage:
puts js
end
# Use Narwhal to run an interactive CoffeeScript session.
def launch_repl
exec "narwhal lib/coffee_script/narwhal/js/launcher.js"
rescue Errno::ENOENT
puts "Error: Narwhal must be installed to use the interactive REPL."
exit(1)
end
# Use Narwhal to compile and execute CoffeeScripts.
def run_scripts
sources = @sources.join(' ')
exec "narwhal lib/coffee_script/narwhal/js/launcher.js #{sources}"
rescue Errno::ENOENT
puts "Error: Narwhal must be installed in order to execute CoffeeScripts."
exit(1)
end
# Print the tokens that the lexer generates from a source script.
def tokens(script)
puts Lexer.new.tokenize(script).inspect
@@ -108,7 +127,7 @@ Usage:
# Compile a single source file to JavaScript.
def compile(script, source='')
begin
CoffeeScript.compile(script)
CoffeeScript.compile(script, :no_wrap => @options[:no_wrap])
rescue CoffeeScript::ParseError => e
STDERR.puts e.message(source)
exit(1) unless @options[:watch]
@@ -134,6 +153,12 @@ Usage:
def parse_options
@options = {}
@option_parser = OptionParser.new do |opts|
opts.on('-i', '--interactive', 'run a CoffeeScript REPL (requires Narwhal)') do |i|
@options[:interactive] = true
end
opts.on('-r', '--run', 'compile and run a script (requires Narwhal)') do |r|
@options[:run] = true
end
opts.on('-o', '--output [DIR]', 'set the directory for compiled JavaScript') do |d|
@options[:output] = d
FileUtils.mkdir_p(d) unless File.exists?(d)
@@ -147,7 +172,7 @@ Usage:
opts.on('-l', '--lint', 'pipe the compiled JavaScript through JSLint') do |l|
@options[:lint] = true
end
opts.on('-e', '--eval', 'eval a little scriptlet directly from the cli') do |e|
opts.on('-e', '--eval', 'compile a cli scriptlet or read from stdin') do |e|
@options[:eval] = true
end
opts.on('-t', '--tokens', 'print the tokens that the lexer produces') do |t|
@@ -156,6 +181,9 @@ Usage:
opts.on('-v', '--verbose', 'print at every step of code generation') do |v|
ENV['VERBOSE'] = 'true'
end
opts.on('-n', '--no-wrap', 'raw output, no safety wrapper or vars') do |n|
@options[:no_wrap] = true
end
opts.on_tail('--install-bundle', 'install the CoffeeScript TextMate bundle') do |i|
install_bundle
exit

View File

@@ -10,8 +10,8 @@ token TRY CATCH FINALLY THROW
token BREAK CONTINUE
token FOR IN WHILE
token SWITCH WHEN
token SUPER
token DELETE
token DELETE INSTANCEOF TYPEOF
token SUPER EXTENDS
token NEWLINE
token COMMENT
token JS
@@ -24,13 +24,13 @@ prechigh
left '<<' '>>' '>>>'
left '&' '|' '^'
left '<=' '<' '>' '>='
right '==' '!=' IS AINT
right '==' '!=' IS ISNT
left '&&' '||' AND OR
right '-=' '+=' '/=' '*='
right DELETE
right '-:' '+:' '/:' '*:' '%:'
right DELETE INSTANCEOF TYPEOF
left "."
right THROW FOR IN WHILE NEW
left UNLESS IF ELSE
right THROW FOR IN WHILE NEW SUPER
left UNLESS IF ELSE EXTENDS
left ":" '||:' '&&:'
right RETURN
preclow
@@ -81,6 +81,7 @@ rule
| While
| For
| Switch
| Extends
| Comment
;
@@ -120,6 +121,7 @@ rule
# Assignment within an object literal.
AssignObj:
IDENTIFIER ":" Expression { result = AssignNode.new(val[0], val[2], :object) }
| STRING ":" Expression { result = AssignNode.new(val[0], val[2], :object) }
| Comment { result = val[0] }
;
@@ -170,21 +172,24 @@ rule
| Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression IS Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression AINT Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression ISNT Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '-:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '+:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '/:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '*:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '%:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '||:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '&&:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| DELETE Expression { result = OpNode.new(val[0], val[1]) }
| TYPEOF Expression { result = OpNode.new(val[0], val[1]) }
| Expression INSTANCEOF Expression { result = OpNode.new(val[1], val[0], val[2]) }
;
# Function definition.
@@ -252,6 +257,11 @@ rule
| Super { result = val[0] }
;
# Extending an object's prototype.
Extends:
Value EXTENDS Expression { result = ExtendsNode.new(val[0], val[2]) }
;
# A generic function invocation.
Invocation:
Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }

View File

@@ -8,14 +8,14 @@ module CoffeeScript
# The list of keywords passed verbatim to the parser.
KEYWORDS = ["if", "else", "then", "unless",
"true", "false", "yes", "no", "on", "off",
"and", "or", "is", "aint", "not",
"and", "or", "is", "isnt", "not",
"new", "return",
"try", "catch", "finally", "throw",
"break", "continue",
"for", "in", "while",
"switch", "when",
"super",
"delete"]
"super", "extends",
"delete", "instanceof", "typeof"]
# Token matching regexes.
IDENTIFIER = /\A([a-zA-Z$_]\w*)/

View File

@@ -0,0 +1,59 @@
# This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.cs
# Executes the `coffee-script` Ruby program to convert from CoffeeScript
# to Javascript. Eventually this will hopefully happen entirely within JS.
# Require external dependencies.
OS: require('os')
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')
# 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").
# 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
while true
try
system.stdout.write('cs> ').flush()
result: exports.evalCS(Readline.readline())
print(result) if result isnt undefined
catch e
print(e)...
# Compile a given CoffeeScript file into JavaScript.
exports.compileFile: path =>
coffee: OS.popen([coffeePath, "--print", "--no-wrap", path])
checkForErrors(coffee)
coffee.stdout.read().
# Compile a string of CoffeeScript into JavaScript.
exports.compile: source =>
coffee: OS.popen([coffeePath, "--eval", "--no-wrap"])
coffee.stdin.write(source).flush().close()
checkForErrors(coffee)
coffee.stdout.read().
# Evaluating a string of CoffeeScript first compiles it externally.
exports.evalCS: source =>
eval(exports.compile(source)).
# Make a factory for the CoffeeScript environment.
exports.makeNarwhalFactory: path =>
code: exports.compileFile(path)
factoryText: "function(require,exports,module,system,print){" + code + "/**/\n}"
if system.engine is "rhino"
Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null)
else
# eval requires parenthesis, but parenthesis break compileFunction.
eval("(" + factoryText + ")")..

View File

@@ -0,0 +1,65 @@
(function(){
// This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.cs Executes the `coffee-script` 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');
// The path to the CoffeeScript Compiler.
var coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee-script');
// Our general-purpose error handler.
var checkForErrors = function(coffeeProcess) {
if (coffeeProcess.wait() === 0) {
return true;
}
system.stderr.print(coffeeProcess.stderr.read());
throw new Error("coffee-script compile error");
};
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every
// command.
exports.run = function(args) {
args.shift();
if (args.length) {
return require(File.absolute(args[0]));
}
while (true) {
try {
system.stdout.write('cs> ').flush();
var result = exports.evalCS(Readline.readline());
if (result !== undefined) {
print(result);
}
} catch (e) {
print(e);
}
}
};
// Compile a given CoffeeScript file into JavaScript.
exports.compileFile = function(path) {
var 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"]);
coffee.stdin.write(source).flush().close();
checkForErrors(coffee);
return coffee.stdout.read();
};
// Evaluating a string of CoffeeScript first compiles it externally.
exports.evalCS = function(source) {
return eval(exports.compile(source));
};
// 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}";
if (system.engine === "rhino") {
return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
} else {
// eval requires parenthesis, but parenthesis break compileFunction.
return eval("(" + factoryText + ")");
}
};
})();

View File

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

View File

@@ -0,0 +1,20 @@
(function(){
// This (javascript) file is generated from lib/coffee_script/narwhal/loader.cs
var coffeescript = null;
var factories = {
};
var 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];
},
// Ensure that the coffee-script environment is loaded.
load: function(topId, path) {
return factories[topId] = factories[topId] || this.reload(topId, path);
}
};
require.loader.loaders.unshift([".cs", loader]);
})();

View File

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

View File

@@ -0,0 +1,19 @@
# This (javascript) file is generated from lib/coffee_script/narwhal/loader.cs
coffeescript: null
factories: {}
loader: {
# Reload the coffee-script environment from source.
reload: topId, path =>
coffeescript ||: require('coffee-script')
factories[topId]: coffeescript.makeNarwhalFactory(path).
# Ensure that the coffee-script environment is loaded.
load: topId, path =>
factories[topId] ||: this.reload(topId, path).
}
require.loader.loaders.unshift([".cs", loader])

View File

@@ -75,16 +75,17 @@ module CoffeeScript
end
# If this is the top-level Expressions, wrap everything in a safety closure.
def root_compile
code = compile(:indent => TAB, :scope => Scope.new)
def root_compile(o={})
indent = o[:no_wrap] ? '' : TAB
code = compile(o.merge(:indent => indent, :scope => Scope.new))
code.gsub!(STRIP_TRAILING_WHITESPACE, '')
"(function(){\n#{code}\n})();"
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={})
return root_compile unless options[:scope]
return root_compile(options) unless options[:scope]
code = @expressions.map { |node|
o = super(options)
if last?(node) && (o[:return] || o[:assign])
@@ -209,10 +210,25 @@ module CoffeeScript
def compile_super(args, o)
methname = o[:last_assign].sub(LEADING_DOT, '')
"this.constructor.prototype.#{methname}.call(this, #{args})"
arg_part = args.empty? ? '' : ", #{args}"
"#{o[:proto_assign]}.prototype.__proto__.#{methname}.call(this#{arg_part})"
end
end
# Node to extend an object's prototype with an ancestor object.
class ExtendsNode < Node
attr_reader :sub_object, :super_object
def initialize(sub_object, super_object)
@sub_object, @super_object = sub_object, super_object
end
def compile(o={})
"#{@sub_object.compile(o)}.prototype.__proto__ = #{@super_object.compile(o)}"
end
end
# A value, indexed or dotted into, or vanilla.
class ValueNode < Node
attr_reader :literal, :properties, :last
@@ -298,7 +314,8 @@ module CoffeeScript
# Setting the value of a local variable, or the value of an object property.
class AssignNode < Node
LEADING_VAR = /\Avar\s+/
LEADING_VAR = /\Avar\s+/
PROTO_ASSIGN = /\A(\S+)\.prototype/
statement
custom_return
@@ -315,16 +332,17 @@ module CoffeeScript
def compile(o={})
o = super(o)
name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable
name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable.to_s
last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s
o = o.merge(:assign => name, :last_assign => last)
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? ? "" : "var #{name};\n#{o[:indent]}"
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 ? name : "var #{name}"
def_part = defined || o[:no_wrap] ? name : "var #{name}"
val_part = @value.compile(o).sub(LEADING_VAR, '')
write("#{def_part} = #{val_part}#{postfix}")
end
@@ -339,10 +357,16 @@ module CoffeeScript
'and' => '&&',
'or' => '||',
'is' => '===',
"aint" => "!==",
"isnt" => "!==",
'not' => '!',
'+:' => '+=',
'-:' => '-=',
'*:' => '*=',
'/:' => '/=',
'%:' => '%='
}
CONDITIONALS = ['||:', '&&:']
CONDITIONALS = ['||:', '&&:']
PREFIX_OPERATORS = ['typeof', 'delete']
attr_reader :operator, :first, :second
@@ -369,7 +393,7 @@ module CoffeeScript
end
def compile_unary(o)
space = @operator.to_s == 'delete' ? ' ' : ''
space = PREFIX_OPERATORS.include?(@operator.to_s) ? ' ' : ''
parts = [@operator.to_s, space, @first.compile(o)]
parts.reverse! if @flip
parts.join('')
@@ -392,6 +416,7 @@ module CoffeeScript
indent = o[:indent]
o[:indent] += TAB
o.delete(:assign)
o.delete(:no_wrap)
@params.each {|id| o[:scope].find(id.to_s) }
code = @body.compile(o)
write("function(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
@@ -454,6 +479,7 @@ module CoffeeScript
def compile(o={})
o = super(o)
o.delete(:return)
indent = o[:indent] + TAB
cond = @condition.compile(o.merge(:no_paren => true))
write("while (#{cond}) {\n#{@body.compile(o.merge(:indent => indent))}\n#{o[:indent]}}")

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@ module CoffeeScript
def initialize(parent=nil)
@parent = parent
@variables = {}
@temp_variable = @parent ? @parent.temp_variable : 'a'
@temp_variable = @parent ? @parent.temp_variable : '__a'
end
# Look up a variable in lexical scope, or declare it if not found.

9
package.json Normal file
View File

@@ -0,0 +1,9 @@
{
"name": "coffee-script",
"lib": "lib/coffee_script/narwhal/js",
"preload": ["loader"],
"description": "Unfancy JavaScript",
"keywords": ["javascript", "language"],
"author": "Jeremy Ashkenas",
"version": "0.1.3"
}

View File

@@ -10,5 +10,5 @@ _.each: obj, iterator, context =>
else
iterator.call(context, obj[key], key, obj) for key in _.keys(obj)..
catch e
throw e if e aint breaker.
throw e if e isnt breaker.
obj.

14
test/fixtures/each.js vendored
View File

@@ -8,16 +8,16 @@
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (_.isArray(obj) || _.isArguments(obj)) {
var a = obj;
for (var b=0, c=a.length; b<c; b++) {
var item = a[b];
var i = b;
var __a = obj;
for (var __b=0, __c=__a.length; __b<__c; __b++) {
var item = __a[__b];
var i = __b;
iterator.call(context, item, i, obj);
}
} else {
var d = _.keys(obj);
for (var e=0, f=d.length; e<f; e++) {
var key = d[e];
var __d = _.keys(obj);
for (var __e=0, __f=__d.length; __e<__f; __e++) {
var key = __d[__e];
iterator.call(context, obj[key], key, obj);
}
}

View File

@@ -1 +1 @@
[[:COMMENT, [" The cornerstone, an each implementation.", " Handles objects implementing forEach, arrays, and raw objects."]], ["\n", "\n"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "each"], [":", ":"], [:PARAM, "obj"], [",", ","], [:PARAM, "iterator"], [",", ","], [:PARAM, "context"], ["=>", "=>"], ["\n", "\n"], [:IDENTIFIER, "index"], [":", ":"], [:NUMBER, "0"], ["\n", "\n"], [:TRY, "try"], ["\n", "\n"], [:IF, "if"], [:IDENTIFIER, "obj"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "forEach"], ["\n", "\n"], [:IDENTIFIER, "obj"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "forEach"], ["(", "("], [:IDENTIFIER, "iterator"], [",", ","], [:IDENTIFIER, "context"], [")", ")"], ["\n", "\n"], [:ELSE, "else"], [:IF, "if"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "isArray"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], [:OR, "or"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "isArguments"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], ["\n", "\n"], [:IDENTIFIER, "iterator"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "call"], ["(", "("], [:IDENTIFIER, "context"], [",", ","], [:IDENTIFIER, "item"], [",", ","], [:IDENTIFIER, "i"], [",", ","], [:IDENTIFIER, "obj"], [")", ")"], [:FOR, "for"], [:IDENTIFIER, "item"], [",", ","], [:IDENTIFIER, "i"], [:IN, "in"], [:IDENTIFIER, "obj"], [".", "."], ["\n", "\n"], [:ELSE, "else"], ["\n", "\n"], [:IDENTIFIER, "iterator"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "call"], ["(", "("], [:IDENTIFIER, "context"], [",", ","], [:IDENTIFIER, "obj"], ["[", "["], [:IDENTIFIER, "key"], ["]", "]"], [",", ","], [:IDENTIFIER, "key"], [",", ","], [:IDENTIFIER, "obj"], [")", ")"], [:FOR, "for"], [:IDENTIFIER, "key"], [:IN, "in"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "keys"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], [".", "."], [".", "."], ["\n", "\n"], [:CATCH, "catch"], [:IDENTIFIER, "e"], ["\n", "\n"], [:THROW, "throw"], [:IDENTIFIER, "e"], [:IF, "if"], [:IDENTIFIER, "e"], [:AINT, "aint"], [:IDENTIFIER, "breaker"], [".", "."], ["\n", "\n"], [:IDENTIFIER, "obj"], [".", "."]]
[[:COMMENT, [" The cornerstone, an each implementation.", " Handles objects implementing forEach, arrays, and raw objects."]], ["\n", "\n"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "each"], [":", ":"], [:PARAM, "obj"], [",", ","], [:PARAM, "iterator"], [",", ","], [:PARAM, "context"], ["=>", "=>"], ["\n", "\n"], [:IDENTIFIER, "index"], [":", ":"], [:NUMBER, "0"], ["\n", "\n"], [:TRY, "try"], ["\n", "\n"], [:IF, "if"], [:IDENTIFIER, "obj"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "forEach"], ["\n", "\n"], [:IDENTIFIER, "obj"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "forEach"], ["(", "("], [:IDENTIFIER, "iterator"], [",", ","], [:IDENTIFIER, "context"], [")", ")"], ["\n", "\n"], [:ELSE, "else"], [:IF, "if"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "isArray"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], [:OR, "or"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "isArguments"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], ["\n", "\n"], [:IDENTIFIER, "iterator"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "call"], ["(", "("], [:IDENTIFIER, "context"], [",", ","], [:IDENTIFIER, "item"], [",", ","], [:IDENTIFIER, "i"], [",", ","], [:IDENTIFIER, "obj"], [")", ")"], [:FOR, "for"], [:IDENTIFIER, "item"], [",", ","], [:IDENTIFIER, "i"], [:IN, "in"], [:IDENTIFIER, "obj"], [".", "."], ["\n", "\n"], [:ELSE, "else"], ["\n", "\n"], [:IDENTIFIER, "iterator"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "call"], ["(", "("], [:IDENTIFIER, "context"], [",", ","], [:IDENTIFIER, "obj"], ["[", "["], [:IDENTIFIER, "key"], ["]", "]"], [",", ","], [:IDENTIFIER, "key"], [",", ","], [:IDENTIFIER, "obj"], [")", ")"], [:FOR, "for"], [:IDENTIFIER, "key"], [:IN, "in"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "keys"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], [".", "."], [".", "."], ["\n", "\n"], [:CATCH, "catch"], [:IDENTIFIER, "e"], ["\n", "\n"], [:THROW, "throw"], [:IDENTIFIER, "e"], [:IF, "if"], [:IDENTIFIER, "e"], [:ISNT, "isnt"], [:IDENTIFIER, "breaker"], [".", "."], ["\n", "\n"], [:IDENTIFIER, "obj"], [".", "."]]

29
test/fixtures/each_no_wrap.js vendored Normal file
View File

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

View File

@@ -1,4 +1,4 @@
nums: n * n for n in [1, 2, 3] if n % 2 aint 0.
nums: n * n for n in [1, 2, 3] if n % 2 isnt 0.
result: n * 2 for n in nums.
print(result.join(',') is '2,18')

View File

@@ -1,21 +0,0 @@
(function(){
var nums;
var a = [1, 2, 3];
var d = [];
for (var b=0, c=a.length; b<c; b++) {
var n = a[b];
if (n % 2 !== 0) {
nums = d.push(n * n);
}
}
nums = d;
var result;
var e = nums;
var h = [];
for (var f=0, g=e.length; f<g; f++) {
n = e[f];
h[f] = n * 2;
}
result = h;
print(result.join(',') === '2,18');
})();

View File

@@ -1,9 +0,0 @@
(function(){
var result;
try {
result = nonexistent * missing;
} catch (error) {
result = true;
}
print(result);
})();

View File

@@ -0,0 +1,23 @@
Base: => .
Base.prototype.func: string =>
'zero/' + string.
FirstChild: => .
FirstChild extends new Base()
FirstChild.prototype.func: string =>
super('one/') + string.
SecondChild: => .
SecondChild extends new FirstChild()
SecondChild.prototype.func: string =>
super('two/') + string.
ThirdChild: => .
ThirdChild extends new SecondChild()
ThirdChild.prototype.func: string =>
super('three/') + string.
result: (new ThirdChild()).func('four')
print(result is 'zero/one/two/three/four')

View File

@@ -1,6 +0,0 @@
(function(){
var a = b = d = true;
var c = false;
var result = a ? b ? c ? false : d ? true : null : null : null;
print(result);
})();

View File

@@ -0,0 +1,10 @@
a: 5
atype: typeof a
b: "hello"
btype: typeof b
Klass: => .
k: new Klass()
print(atype is 'number' and btype is 'string' and k instanceof Klass)

View File

@@ -6,7 +6,7 @@ func: =>
a--.
c: {
text: b
"text": b
}
c: 'error' unless 42 > 41
@@ -16,7 +16,7 @@ func: =>
else
c.text + '---'.
c.list: let for let in c.text.split('') if let is '-'.
c.list: l for l in c.text.split('') if l is '-'.
c.single: c.list[1, 1][0].

View File

@@ -1,29 +0,0 @@
(function(){
var func = function() {
var a = 3;
var b = [];
while (a >= 0) {
b.push('o');
a--;
}
var c = {
text: b
};
if (!(42 > 41)) {
c = 'error';
}
c.text = false ? 'error' : c.text + '---';
var d = c.text.split('');
var g = [];
for (var e=0, f=d.length; e<f; e++) {
var let = d[e];
if (let === '-') {
c.list = g.push(let);
}
}
c.list = g;
c.single = c.list.slice(1, 1 + 1)[0];
return c.single;
};
print(func() === '-');
})();

View File

@@ -1,16 +0,0 @@
(function(){
var num = 10;
var result;
if (num === 5) {
result = false;
} else if (num === 'a') {
result = false;
} else if (num === 10) {
result = true;
} else if (num === 11) {
result = false;
} else {
result = false;
}
print(result);
})();

View File

@@ -3,18 +3,11 @@ 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/
def test_execution_of_coffeescript
`bin/coffee-script test/fixtures/execution/*.cs`
sources = Dir['test/fixtures/execution/*.js'].map {|f| File.expand_path(f) }
starting_place = File.expand_path(Dir.pwd)
Dir.chdir('/Users/jashkenas/Desktop/Beauty/Code/v8')
sources.each do |source|
# puts `./shell #{source}`
assert `./shell #{source}`.chomp.to_sym == :true
end
ensure
Dir.chdir(starting_place)
sources = ['test/fixtures/execution/*.cs'].join(' ')
assert `bin/coffee-script -r #{sources}`.match(ALLS_WELL)
end
def test_lintless_coffeescript

View File

@@ -72,4 +72,9 @@ class ParserTest < Test::Unit::TestCase
assert nodes.compile == File.read('test/fixtures/each.js')
end
def test_no_wrap
nodes = @par.parse(File.read('test/fixtures/each.cs'))
assert nodes.compile(:no_wrap => true) == File.read('test/fixtures/each_no_wrap.js')
end
end