Compare commits

..

53 Commits
0.1.1 ... 0.1.5

Author SHA1 Message Date
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
Jeremy Ashkenas
88fe4f6fd1 CoffeeScript 0.1.4 2009-12-25 14:43:24 -08:00
Jeremy Ashkenas
03a90928e1 moved the coffeescript extension over from .cs to .coffee -- let's leave C# in peace. Changed array comprehensions to always return their mapped result, even when unassigned 2009-12-25 14:18:05 -08:00
Jeremy Ashkenas
67865d3341 stopped using __proto__, instead, using a variant of goog.inherits for extends and super() 2009-12-25 13:57:47 -08:00
Jeremy Ashkenas
0337513172 ForBody is really the ForSource 2009-12-25 13:40:57 -08:00
Jeremy Ashkenas
1ee2c53391 cleaned up the for grammar and eliminated a shift/reduce conflict 2009-12-25 13:39:33 -08:00
Jeremy Ashkenas
4b7c965101 make equals signs full equals of colons -- you can use them inside of object literals now too 2009-12-25 13:21:17 -08:00
Jeremy Ashkenas
11c394fb7e allowing = to assign 2009-12-25 07:42:27 -08:00
Jeremy Ashkenas
54a7c405e7 going back to familiar operators +: is just too strange 2009-12-25 07:31:51 -08:00
Jeremy Ashkenas
781f3b5fa4 added a test to make sure that chained calls work 2009-12-25 07:16:59 -08:00
Jeremy Ashkenas
2393472924 allowing chained function calls, one right after another 2009-12-25 07:08:57 -08:00
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
92 changed files with 2418 additions and 1694 deletions

2
README
View File

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

View File

@@ -10,15 +10,24 @@ 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, :racc_args do |t, args|
sh "racc #{args[:racc_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 lib/coffee_script/narwhal/*.coffee -o lib/coffee_script/narwhal/js"
end
end
desc "Build the documentation page"
task :doc do
source = 'documentation/index.html.erb'
child = fork { exec "bin/coffee-script documentation/cs/*.cs -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.1' # Keep version in sync with coffee-script.rb
s.date = '2009-12-24'
s.version = '0.1.5' # Keep version in sync with coffee-script.rb
s.date = '2009-12-26'
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

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

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

@@ -6,4 +6,4 @@ if happy and knows_it
date: if friday then sue else jill.
expensive ||: do_the_math()
expensive ||= do_the_math()

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

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

@@ -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 Animal
Snake.prototype.move: =>
alert("Slithering...")
super(5).
Horse: name => this.name: name.
Horse.prototype: new Animal()
Horse extends Animal
Horse.prototype.move: =>
alert("Galloping...")
super(45).

View File

@@ -1,3 +0,0 @@
# CoffeeScript on the left, JS on the right.
square: x => x * x.

View File

@@ -1,11 +0,0 @@
# Comments start with hash marks.
# Periods mark the end of a block.
left_hand: if raining then umbrella else parasol.
# To signal the beginning of the next expression,
# use "then", or a newline.
left_hand: if raining
umbrella
else
parasol.

View File

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

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

@@ -3,7 +3,7 @@
def code_for(file, executable=false)
@stripper ||= /(\A\(function\(\)\{\n|\}\)\(\);\Z|^ )/
return '' unless File.exists?("documentation/js/#{file}.js")
cs = File.read("documentation/cs/#{file}.cs")
cs = File.read("documentation/coffee/#{file}.coffee")
js = File.read("documentation/js/#{file}.js").gsub(@stripper, '')
cshtml = Uv.parse(cs, 'xhtml', 'coffeescript', false, 'idle', false)
jshtml = Uv.parse(js, 'xhtml', 'javascript', false, 'idle', false)
@@ -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
@@ -66,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="#super">Calling Super from a Subclass</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>
@@ -83,7 +82,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.
@@ -93,16 +92,33 @@
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
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</tt>
command also provides direct evaluation and an interactive REPL.
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>
<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 +141,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>
@@ -133,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>
@@ -151,6 +167,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>
@@ -164,9 +188,10 @@ gem install coffee-script</pre>
</p>
<pre>
coffee-script path/to/script.cs
coffee-script --watch --lint experimental.cs
coffee-script --print app/scripts/*.cs > 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>
@@ -186,15 +211,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)') %>
@@ -206,6 +231,10 @@ coffee-script --print app/scripts/*.cs > 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>
@@ -250,9 +279,9 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
</p>
<%= code_for('conditionals') %>
<p>
The conditional assignment operators are available: <tt>||:</tt>,
The conditional assignment operators are available: <tt>||=</tt>,
which only assigns a value to a variable if the variable's current value
is falsy, and <tt>&amp;&amp;:</tt>, which only replaces the value of
is falsy, and <tt>&amp;&amp;=</tt>, which only replaces the value of
truthy variables.
</p>
@@ -269,8 +298,13 @@ coffee-script --print app/scripts/*.cs > 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>
Because the <tt>==</tt> operator frequently causes undesirable coercion,
@@ -278,14 +312,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>
@@ -319,17 +357,25 @@ coffee-script --print app/scripts/*.cs > 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="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 +383,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>
@@ -377,13 +425,20 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
</p>
<%= code_for('strings', 'moby_dick') %>
<h2 id="contributing">Contributing</h2>
<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>
<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
@@ -391,14 +446,15 @@ coffee-script --print app/scripts/*.cs > 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
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>
@@ -406,16 +462,68 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
having a JavaScript version of the compiler).
</li>
<li>
A lot of the code generation in <tt>nodes.rb</tt> gets into messy
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.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
<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.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.3</b>
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.
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

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

View File

@@ -1,19 +1,20 @@
(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];
d[b] = food.eat();
__a = ['toast', 'cheese', 'wine'];
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
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;
i % 2 === 0 ? highlight(row) : null;
__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,12 +22,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);
__a = list;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
num = __a[__b];
__d[__b] = math.cube(num);
}
cubed_list = d;
cubed_list = __d;
})();

View File

@@ -1,9 +1,11 @@
(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;
new_num = 3;
return new_num;
};
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,29 +1,34 @@
(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) {
Snake = function(name) {
this.name = name;
return this.name;
};
Snake.__superClass__ = Animal.prototype;
Snake.prototype = new Animal();
Snake.prototype.constructor = Snake;
Snake.prototype.move = function() {
alert("Slithering...");
return this.constructor.prototype.move.call(this, 5);
return Snake.__superClass__.move.call(this, 5);
};
var Horse = function(name) {
Horse = function(name) {
this.name = name;
return this.name;
};
Horse.__superClass__ = Animal.prototype;
Horse.prototype = new Animal();
Horse.prototype.constructor = Horse;
Horse.prototype.move = function() {
alert("Galloping...");
return this.constructor.prototype.move.call(this, 45);
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

@@ -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(). )
@@ -62,8 +62,8 @@ race: =>
race().
# Conditional assignment:
good ||: evil
wine &&: cheese
good ||= evil
wine &&= cheese
# Nested property access and calls.
((moon.turn(360))).shapes[3].move({x: 45, y: 30}).position['top'].offset('x')
@@ -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,
@@ -145,13 +145,13 @@ Animal.prototype.move: meters =>
alert(this.name + " moved " + meters + "m.").
Snake: name => this.name: name.
Snake.prototype: Animal
Snake extends Animal
Snake.prototype.move: =>
alert('Slithering...')
super(5).
Horse: name => this.name: name.
Horse.prototype: Animal
Horse extends Animal
Horse.prototype.move: =>
alert('Galloping...')
super(45).

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

@@ -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
@@ -52,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="#super">Calling Super from a Subclass</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>
@@ -88,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>) {
@@ -111,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];
d[b] <span class="Keyword">=</span> math.cube(num);
__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:
cubed_list <span class="Keyword">=</span> __d;
</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) {
@@ -142,18 +140,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);
__a = list;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
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.
@@ -163,16 +160,33 @@ cubed_list = d;
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
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</tt>
command also provides direct evaluation and an interactive REPL.
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>
<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 +209,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>
@@ -203,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>
@@ -221,6 +235,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>
@@ -234,9 +256,10 @@ gem install coffee-script</pre>
</p>
<pre>
coffee-script path/to/script.cs
coffee-script --watch --lint experimental.cs
coffee-script --print app/scripts/*.cs > 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>
@@ -256,29 +279,31 @@ 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.
<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>
@@ -291,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>
@@ -310,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
@@ -335,20 +368,24 @@ 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>;
new_num <span class="Keyword">=</span> <span class="Number">3</span>;
<span class="Keyword">return</span> new_num;
};
<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;
new_num = 3;
return new_num;
};
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
@@ -383,8 +420,8 @@ 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;
expensive <span class="Keyword">||</span><span class="Keyword">=</span> do_the_math()
</pre><pre class="idle"><span class="Storage">var</span> date, mood;
<span class="Keyword">if</span> (singing) {
mood <span class="Keyword">=</span> greatly_improved;
}
@@ -392,13 +429,13 @@ 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>
The conditional assignment operators are available: <tt>||:</tt>,
The conditional assignment operators are available: <tt>||=</tt>,
which only assigns a value to a variable if the variable's current value
is falsy, and <tt>&amp;&amp;:</tt>, which only replaces the value of
is falsy, and <tt>&amp;&amp;=</tt>, which only replaces the value of
truthy variables.
</p>
@@ -420,7 +457,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) {
@@ -429,8 +467,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) {
@@ -439,13 +478,23 @@ 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>
Because the <tt>==</tt> operator frequently causes undesirable coercion,
@@ -453,14 +502,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,15 +525,15 @@ 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>
<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>;
}
@@ -528,41 +581,58 @@ 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];
d[b] <span class="Keyword">=</span> food.eat();
__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;
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;
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>;
__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>]
<p id="super">
<b class="header">Calling Super from a Subclass</b>
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>
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 +640,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 +653,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> Animal
<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> Animal
<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>).
@@ -601,58 +673,68 @@ 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="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>;
};
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>();
<span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">constructor</span> = Snake;
<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.__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="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>;
};
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>();
<span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">constructor</span> = Horse;
<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.__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) {
Snake = function(name) {
this.name = name;
return this.name;
};
Snake.__superClass__ = Animal.prototype;
Snake.prototype = new Animal();
Snake.prototype.constructor = Snake;
Snake.prototype.move = function() {
alert("Slithering...");
return this.constructor.prototype.move.call(this, 5);
return Snake.__superClass__.move.call(this, 5);
};
var Horse = function(name) {
Horse = function(name) {
this.name = name;
return this.name;
};
Horse.__superClass__ = Animal.prototype;
Horse.prototype = new Animal();
Horse.prototype.constructor = Horse;
Horse.prototype.move = function() {
alert("Galloping...");
return this.constructor.prototype.move.call(this, 45);
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>
@@ -666,10 +748,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>
@@ -742,13 +827,16 @@ 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 \
@@ -756,13 +844,20 @@ 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>
<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>
<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
@@ -770,14 +865,15 @@ 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
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>
@@ -785,16 +881,68 @@ world...";
having a JavaScript version of the compiler).
</li>
<li>
A lot of the code generation in <tt>nodes.rb</tt> gets into messy
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.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
<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.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.3</b>
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.
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.1' # Keep in sync with the gemspec.
VERSION = '0.1.5' # 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

@@ -5,7 +5,7 @@
<key>name</key>
<string>comments</string>
<key>scope</key>
<string>source.cs</string>
<string>source.coffee</string>
<key>settings</key>
<dict>
<key>shellVariables</key>

View File

@@ -6,8 +6,7 @@
<string>CoffeeScript Syntax: version 1</string>
<key>fileTypes</key>
<array>
<string>cs</string>
<string>coffeescript</string>
<string>coffee</string>
</array>
<key>name</key>
<string>CoffeeScript</string>
@@ -19,22 +18,22 @@
<key>1</key>
<dict>
<key>name</key>
<string>entity.name.function.cs</string>
<string>entity.name.function.coffee</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>keyword.operator.cs</string>
<string>keyword.operator.coffee</string>
</dict>
<key>3</key>
<dict>
<key>name</key>
<string>variable.parameter.function.cs</string>
<string>variable.parameter.function.coffee</string>
</dict>
<key>4</key>
<dict>
<key>name</key>
<string>storage.type.function.cs</string>
<string>storage.type.function.coffee</string>
</dict>
</dict>
<key>comment</key>
@@ -42,7 +41,7 @@
<key>match</key>
<string>([a-zA-Z_?.$]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</string>
<key>name</key>
<string>meta.function.cs</string>
<string>meta.function.coffee</string>
</dict>
<dict>
<key>captures</key>
@@ -50,12 +49,12 @@
<key>1</key>
<dict>
<key>name</key>
<string>variable.parameter.function.cs</string>
<string>variable.parameter.function.coffee</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>storage.type.function.cs</string>
<string>storage.type.function.coffee</string>
</dict>
</dict>
<key>comment</key>
@@ -63,7 +62,7 @@
<key>match</key>
<string>([a-zA-Z_?., $]*)\s*(=&gt;)</string>
<key>name</key>
<string>meta.inline.function.cs</string>
<string>meta.inline.function.coffee</string>
</dict>
<dict>
<key>captures</key>
@@ -71,12 +70,12 @@
<key>1</key>
<dict>
<key>name</key>
<string>keyword.operator.new.cs</string>
<string>keyword.operator.new.coffee</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>entity.name.type.instance.cs</string>
<string>entity.name.type.instance.coffee</string>
</dict>
</dict>
<key>match</key>
@@ -88,7 +87,7 @@
<key>match</key>
<string>\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?))\b</string>
<key>name</key>
<string>constant.numeric.cs</string>
<string>constant.numeric.coffee</string>
</dict>
<dict>
<key>begin</key>
@@ -98,7 +97,7 @@
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.cs</string>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>end</key>
@@ -108,18 +107,18 @@
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.cs</string>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.single.cs</string>
<string>string.quoted.single.coffee</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)</string>
<key>name</key>
<string>constant.character.escape.cs</string>
<string>constant.character.escape.coffee</string>
</dict>
</array>
</dict>
@@ -131,7 +130,7 @@
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.cs</string>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>end</key>
@@ -141,18 +140,18 @@
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.cs</string>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.double.cs</string>
<string>string.quoted.double.coffee</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)</string>
<key>name</key>
<string>constant.character.escape.cs</string>
<string>constant.character.escape.coffee</string>
</dict>
</array>
</dict>
@@ -164,7 +163,7 @@
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.cs</string>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>end</key>
@@ -174,18 +173,18 @@
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.cs</string>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.script.cs</string>
<string>string.quoted.script.coffee</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)</string>
<key>name</key>
<string>constant.character.escape.cs</string>
<string>constant.character.escape.coffee</string>
</dict>
</array>
</dict>
@@ -195,61 +194,61 @@
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.cs</string>
<string>punctuation.definition.comment.coffee</string>
</dict>
</dict>
<key>match</key>
<string>(#).*$\n?</string>
<key>name</key>
<string>comment.line.cs</string>
<string>comment.line.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(break|when|catch|continue|else|finally|for|if|return|switch|then|throw|try|unless|while)\b</string>
<key>name</key>
<string>keyword.control.cs</string>
<string>keyword.control.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(true|on|yes)\b</string>
<key>name</key>
<string>constant.language.boolean.true.cs</string>
<string>constant.language.boolean.true.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(false|off|no)\b</string>
<key>name</key>
<string>constant.language.boolean.false.cs</string>
<string>constant.language.boolean.false.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\bnull\b</string>
<key>name</key>
<string>constant.language.null.cs</string>
<string>constant.language.null.coffee</string>
</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>
<string>variable.language.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(debugger)\b</string>
<string>\b(debugger|\\)\b</string>
<key>name</key>
<string>keyword.other.cs</string>
<string>keyword.other.coffee</string>
</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>
<string>keyword.operator.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\b(Infinity|NaN|undefined)\b</string>
<key>name</key>
<string>constant.language.cs</string>
<string>constant.language.coffee</string>
</dict>
<dict>
<key>begin</key>
@@ -259,7 +258,7 @@
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.cs</string>
<string>punctuation.definition.string.begin.coffee</string>
</dict>
</dict>
<key>end</key>
@@ -269,18 +268,18 @@
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.cs</string>
<string>punctuation.definition.string.end.coffee</string>
</dict>
</dict>
<key>name</key>
<string>string.regexp.cs</string>
<string>string.regexp.coffee</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\.</string>
<key>name</key>
<string>constant.character.escape.cs</string>
<string>constant.character.escape.coffee</string>
</dict>
</array>
</dict>
@@ -288,41 +287,41 @@
<key>match</key>
<string>\;</string>
<key>name</key>
<string>punctuation.terminator.statement.cs</string>
<string>punctuation.terminator.statement.coffee</string>
</dict>
<dict>
<key>match</key>
<string>,[ |\t]*</string>
<key>name</key>
<string>meta.delimiter.object.comma.cs</string>
<string>meta.delimiter.object.comma.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\.</string>
<key>name</key>
<string>meta.delimiter.method.period.cs</string>
<string>meta.delimiter.method.period.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\{|\}</string>
<key>name</key>
<string>meta.brace.curly.cs</string>
<string>meta.brace.curly.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\(|\)</string>
<key>name</key>
<string>meta.brace.round.cs</string>
<string>meta.brace.round.coffee</string>
</dict>
<dict>
<key>match</key>
<string>\[|\]</string>
<key>name</key>
<string>meta.brace.square.cs</string>
<string>meta.brace.square.coffee</string>
</dict>
</array>
<key>scopeName</key>
<string>source.cs</string>
<string>source.coffee</string>
<key>uuid</key>
<string>5B520980-A7D5-4E10-8582-1A4C889A8DE5</string>
</dict>

View File

@@ -5,15 +5,15 @@ 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.cs
coffee path/to/script.coffee
EOS
# Seconds to pause between checks for changed source files.
@@ -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,17 +102,36 @@ 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
end
# Compile a single source file to JavaScript.
def compile(script, source='')
def compile(script, source='error')
begin
CoffeeScript.compile(script)
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
@@ -134,6 +155,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 +174,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,12 +183,15 @@ 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
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

@@ -11,7 +11,7 @@ token BREAK CONTINUE
token FOR IN WHILE
token SWITCH WHEN
token DELETE INSTANCEOF TYPEOF
token SUPER
token SUPER EXTENDS
token NEWLINE
token COMMENT
token JS
@@ -24,20 +24,20 @@ prechigh
left '<<' '>>' '>>>'
left '&' '|' '^'
left '<=' '<' '>' '>='
right '==' '!=' IS AINT
right '==' '!=' IS ISNT
left '&&' '||' AND OR
right '-=' '+=' '/=' '*='
right '-=' '+=' '/=' '*=' '%='
right DELETE INSTANCEOF TYPEOF
left "."
right THROW FOR IN WHILE NEW
left UNLESS IF ELSE
left ":" '||:' '&&:'
left '.'
right THROW FOR IN WHILE NEW SUPER
left UNLESS IF ELSE EXTENDS
left ASSIGN '||=' '&&='
right RETURN
preclow
# We expect 4 shift/reduce errors for optional syntax.
# We expect 3 shift/reduce errors for optional syntax.
# There used to be 252 -- greatly improved.
expect 4
expect 3
rule
@@ -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.
@@ -81,6 +81,7 @@ rule
| While
| For
| Switch
| Extends
| Comment
;
@@ -114,12 +115,13 @@ rule
# Assignment to a variable.
Assign:
Value ":" Expression { result = AssignNode.new(val[0], val[2]) }
Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
;
# Assignment within an object literal.
AssignObj:
IDENTIFIER ":" 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] }
;
@@ -142,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]) }
@@ -170,7 +172,7 @@ rule
| Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression 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]) }
@@ -181,8 +183,9 @@ rule
| Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '||:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '&&:' 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]) }
@@ -210,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]) }
@@ -221,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.
@@ -229,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]) }
@@ -254,9 +253,15 @@ rule
| Super { result = val[0] }
;
# Extending an object's prototype.
Extends:
Value EXTENDS Value { result = ExtendsNode.new(val[0], val[2]) }
;
# A generic function invocation.
Invocation:
Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
| Invocation "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
;
# Calling super.
@@ -264,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]) }
@@ -307,19 +318,23 @@ rule
;
# Array comprehensions, including guard and current index.
# Looks a little confusing, check nodes.rb for the arguments to ForNode.
For:
Expression FOR IDENTIFIER
IN PureExpression "." { result = ForNode.new(val[0], val[4], val[2], nil) }
| Expression FOR
IDENTIFIER "," IDENTIFIER
IN PureExpression "." { result = ForNode.new(val[0], val[6], val[2], nil, val[4]) }
| Expression FOR IDENTIFIER
IN PureExpression
IF Expression "." { result = ForNode.new(val[0], val[4], val[2], val[6]) }
| Expression FOR
IDENTIFIER "," IDENTIFIER
IN PureExpression
IF Expression "." { result = ForNode.new(val[0], val[6], val[2], val[8], val[4]) }
Expression FOR
ForVariables ForSource { result = ForNode.new(val[0], val[3][0], val[2][0], val[3][1], val[2][1]) }
;
# An array comprehension has variables for the current element and index.
ForVariables:
IDENTIFIER { result = val }
| IDENTIFIER "," IDENTIFIER { result = [val[0], val[2]] }
;
# The source of the array comprehension can optionally be filtered.
ForSource:
IN PureExpression "." { result = [val[1]] }
| IN PureExpression
IF Expression "." { result = [val[1], val[3]] }
;
# Switch/When blocks.

View File

@@ -8,18 +8,18 @@ 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",
"super", "extends",
"delete", "instanceof", "typeof"]
# 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([+\*&|\/\-%=<>:!]+)/
@@ -40,6 +40,9 @@ module CoffeeScript
# Tokens that always constitute the end of an expression.
EXP_END = ['}', ')', ']']
# Assignment tokens.
ASSIGN = [':', '=']
# Scan by attempting to match tokens one character at a time. Slow and steady.
def tokenize(code)
@code = code.chomp # Cleanup code by remove extra line breaks
@@ -72,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
@@ -127,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]
@@ -139,7 +144,8 @@ module CoffeeScript
value ||= @chunk[0,1]
skip_following_newlines if EXP_START.include?(value)
remove_leading_newlines if EXP_END.include?(value)
token(value, value)
tag = ASSIGN.include?(value) ? :ASSIGN : value
token(tag, value)
@i += value.length
end

View File

@@ -0,0 +1,59 @@
# 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.
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')
# Our general-purpose error handler.
checkForErrors: coffeeProcess =>
return true if coffeeProcess.wait() is 0
system.stderr.print(coffeeProcess.stderr.read())
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
while true
try
system.stdout.write('coffee> ').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,69 @@
(function(){
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.
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');
// Our general-purpose error handler.
checkForErrors = function(coffeeProcess) {
if (coffeeProcess.wait() === 0) {
return true;
}
system.stderr.print(coffeeProcess.stderr.read());
throw new Error("CoffeeScript compile error");
};
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every
// command.
exports.run = function(args) {
var result;
args.shift();
if (args.length) {
return require(File.absolute(args[0]));
}
while (true) {
try {
system.stdout.write('coffee> ').flush();
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;
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;
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, factoryText;
code = exports.compileFile(path);
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(){
var coffeescript, factories, loader;
// This (javascript) file is generated from lib/coffee_script/narwhal/loader.coffee
coffeescript = null;
factories = {
};
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([".coffee", 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.coffee
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([".coffee", loader])

View File

@@ -75,17 +75,20 @@ 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), o[:no_wrap] ? nil : :code)
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]
code = @expressions.map { |node|
# 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]
compiled = @expressions.map do |node|
o = super(options)
if last?(node) && (o[:return] || o[:assign])
if o[:return]
@@ -98,14 +101,17 @@ module CoffeeScript
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
@@ -201,7 +207,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})")
@@ -209,10 +215,29 @@ 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]}.__superClass__.#{methname}.call(this#{arg_part})"
end
end
# Node to extend an object's prototype with an ancestor object.
# After goog.inherits from the Closure Library.
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, sup = @sub_object.compile(o), @super_object.compile(o)
"#{sub}.__superClass__ = #{sup}.prototype;\n#{o[:indent]}" +
"#{sub}.prototype = new #{sup}();\n#{o[:indent]}" +
"#{sub}.prototype.constructor = #{sub}"
end
end
# A value, indexed or dotted into, or vanilla.
class ValueNode < Node
attr_reader :literal, :properties, :last
@@ -280,27 +305,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
@@ -315,18 +373,16 @@ module CoffeeScript
def compile(o={})
o = super(o)
name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable
last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s
o = o.merge(:assign => name, :last_assign => last)
name = @variable.compile(o)
last = @variable.last.to_s
proto = name[PROTO_ASSIGN, 1]
o = o.merge(:assign => @variable, :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)}") 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]}"
return write(def_part + @value.compile(o)) if @value.custom_assign?
def_part = defined ? name : "var #{name}"
val_part = @value.compile(o).sub(LEADING_VAR, '')
write("#{def_part} = #{val_part}#{postfix}")
o[:scope].find(name) unless @variable.properties?
return write(@value.compile(o)) if @value.custom_assign?
write("#{name} = #{@value.compile(o)}#{postfix}")
end
end
@@ -339,10 +395,10 @@ module CoffeeScript
'and' => '&&',
'or' => '||',
'is' => '===',
"aint" => "!==",
'not' => '!',
"isnt" => "!==",
'not' => '!'
}
CONDITIONALS = ['||:', '&&:']
CONDITIONALS = ['||=', '&&=']
PREFIX_OPERATORS = ['typeof', 'delete']
attr_reader :operator, :first, :second
@@ -393,8 +449,9 @@ module CoffeeScript
indent = o[:indent]
o[:indent] += TAB
o.delete(:assign)
@params.each {|id| o[:scope].find(id.to_s) }
code = @body.compile(o)
o.delete(:no_wrap)
@params.each {|id| o[:scope].parameter(id.to_s) }
code = @body.compile(o, :code)
write("function(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
end
end
@@ -455,8 +512,9 @@ module CoffeeScript
def compile(o={})
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
@@ -482,32 +540,37 @@ module CoffeeScript
def compile(o={})
o = super(o)
scope = o[:scope]
name_found = scope.find(@name)
index_found = @index && scope.find(@index)
svar = scope.free_variable
ivar = scope.free_variable
lvar = 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" : ''
range = @source.is_a?(RangeNode)
scope = o[:scope]
name_found = scope.find(@name)
index_found = @index && scope.find(@index)
svar = scope.free_variable
ivar = range ? name : scope.free_variable
lvar = scope.free_variable
rvar = scope.free_variable
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 = "#{rvar} = [];\n#{o[:indent]}"
save_result = "#{rvar}[#{index_var || ivar}] = "
return_result = rvar
set_result = ''
save_result = ''
return_result = ''
body = @body
suffix = ';'
if o[:return] || o[:assign]
rvar = scope.free_variable
set_result = "var #{rvar} = [];\n#{o[:indent]}"
save_result += "#{rvar}[#{ivar}] = "
return_result = rvar
return_result = "#{o[:assign]} = #{return_result};" if o[:assign]
return_result = "return #{return_result};" if o[:return]
return_result = "\n#{o[:indent]}#{return_result}"
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])
body = IfNode.new(@filter, body, nil, :statement => true)
@@ -518,9 +581,10 @@ module CoffeeScript
body = IfNode.new(@filter, @body)
end
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
@@ -567,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
@@ -576,7 +642,7 @@ module CoffeeScript
end
def statement?
@expressions.statement?
@expressions.unwrap.statement?
end
def custom_assign?
@@ -588,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

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,23 +5,29 @@ 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)
@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.
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

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.5"
}

31
test/fixtures/each.js vendored
View File

@@ -1,31 +0,0 @@
(function(){
// 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 +0,0 @@
[[: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"], [".", "."]]

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

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

@@ -1,10 +0,0 @@
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

@@ -1,10 +0,0 @@
(function(){
var a = 5;
var atype = typeof a;
var b = "hello";
var btype = typeof b;
var Klass = function() {
};
var k = new Klass();
print(atype === 'number' && btype === 'string' && k instanceof Klass);
})();

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

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

View File

@@ -0,0 +1,5 @@
identity_wrap: x => => x..
result: identity_wrap(identity_wrap(true))()()
print(result)

View File

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

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

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

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

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

36
test/fixtures/generation/each.js vendored Normal file
View File

@@ -0,0 +1,36 @@
(function(){
// The cornerstone, an each implementation.
// Handles objects implementing forEach, arrays, and raw objects.
_.each = function(obj, iterator, context) {
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)) {
__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 {
__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;
}
} catch (e) {
if (e !== breaker) {
throw e;
}
}
return obj;
};
})();

1
test/fixtures/generation/each.tokens vendored Normal file
View File

@@ -0,0 +1 @@
[[:COMMENT, [" The cornerstone, an each implementation.", " Handles objects implementing forEach, arrays, and raw objects."]], ["\n", "\n"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "each"], [:ASSIGN, ":"], [:PARAM, "obj"], [",", ","], [:PARAM, "iterator"], [",", ","], [:PARAM, "context"], ["=>", "=>"], ["\n", "\n"], [:IDENTIFIER, "index"], [:ASSIGN, ":"], [: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"], [".", "."]]

View File

@@ -0,0 +1,34 @@
// The cornerstone, an each implementation.
// Handles objects implementing forEach, arrays, and raw objects.
_.each = function(obj, iterator, context) {
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)) {
__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 {
__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;
}
} catch (e) {
if (e !== breaker) {
throw e;
}
}
return obj;
};

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,27 +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/m
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/*.coffee'].join(' ')
assert `bin/coffee -r #{sources}`.match(ALLS_WELL)
end
def test_lintless_coffeescript
lint_results = `bin/coffee-script -l test/fixtures/execution/*.cs`
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/*.cs`
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

@@ -12,14 +12,14 @@ class LexerTest < Test::Unit::TestCase
def test_lexing_basic_assignment
code = "a: 'one'; b: [1, 2]"
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [":", ":"],
[:STRING, "'one'"], [";", ";"], [:IDENTIFIER, "b"], [":", ":"],
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"],
[:STRING, "'one'"], [";", ";"], [:IDENTIFIER, "b"], [:ASSIGN, ":"],
["[", "["], [:NUMBER, "1"], [",", ","], [:NUMBER, "2"], ["]", "]"]]
end
def test_lexing_object_literal
code = "{one : 1}"
assert @lex.tokenize(code) == [["{", "{"], [:IDENTIFIER, "one"], [":", ":"],
assert @lex.tokenize(code) == [["{", "{"], [:IDENTIFIER, "one"], [:ASSIGN, ":"],
[:NUMBER, "1"], ["}", "}"]]
end
@@ -37,14 +37,20 @@ 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"], [":", ":"], [:NUMBER, "1"],
["\n", "\n"], [:COMMENT, [" comment", " on two lines"]], ["\n", "\n"],
[:IDENTIFIER, "b"], [":", ":"], [:NUMBER, "2"]]
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"], [:NUMBER, "1"],
["\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.cs'))
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,18 +58,29 @@ class ParserTest < Test::Unit::TestCase
end
def test_parsing_inner_comments
nodes = @par.parse(File.read('test/fixtures/inner_comments.cs'))
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.cs'))
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/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