mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-13 16:57:54 -05:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fe6fa1cd7 | ||
|
|
835db4b279 | ||
|
|
f89c864911 | ||
|
|
542726911a | ||
|
|
575dc7d12e | ||
|
|
ff0062b088 | ||
|
|
78589f5db1 | ||
|
|
903331f3ff | ||
|
|
47b45c4494 | ||
|
|
c4844abb28 | ||
|
|
96ae6d80f3 | ||
|
|
60342e8cd9 | ||
|
|
f9c3d3fc14 | ||
|
|
b1e25eea88 | ||
|
|
1486bbab9f | ||
|
|
59d912cc26 | ||
|
|
9adf2e2d30 | ||
|
|
cf46fa8c2c | ||
|
|
16ca3d1608 | ||
|
|
476a251c80 | ||
|
|
274152aff7 | ||
|
|
00659e5f76 | ||
|
|
88fe4f6fd1 | ||
|
|
03a90928e1 | ||
|
|
67865d3341 | ||
|
|
0337513172 | ||
|
|
1ee2c53391 | ||
|
|
4b7c965101 | ||
|
|
11c394fb7e | ||
|
|
54a7c405e7 | ||
|
|
781f3b5fa4 | ||
|
|
2393472924 | ||
|
|
859ab7583f | ||
|
|
3eedd5bb50 | ||
|
|
5b7e695f6c | ||
|
|
1e3182727b | ||
|
|
e595dbfcee | ||
|
|
12b830893d | ||
|
|
cca80342aa | ||
|
|
4412f590cf | ||
|
|
0cd2d78027 | ||
|
|
be672ebfc1 | ||
|
|
9047c87567 | ||
|
|
31d630bb91 | ||
|
|
7a2f5a333f | ||
|
|
66303636dc |
2
README
2
README
@@ -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/
|
||||
|
||||
17
Rakefile
17
Rakefile
@@ -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
|
||||
|
||||
3
bin/cs
3
bin/cs
@@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env narwhal
|
||||
|
||||
require("coffee-script").run(system.args);
|
||||
@@ -1,7 +1,7 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'coffee-script'
|
||||
s.version = '0.1.2' # Keep version in sync with coffee-script.rb
|
||||
s.date = '2009-12-24'
|
||||
s.version = '0.1.6' # Keep version in sync with coffee-script.rb
|
||||
s.date = '2009-12-27'
|
||||
|
||||
s.homepage = "http://jashkenas.github.com/coffee-script/"
|
||||
s.summary = "The CoffeeScript Compiler"
|
||||
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
|
||||
s.has_rdoc = false
|
||||
|
||||
s.require_paths = ['lib']
|
||||
s.executables = ['coffee-script']
|
||||
s.executables = ['coffee']
|
||||
|
||||
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README']
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -6,4 +6,4 @@ if happy and knows_it
|
||||
|
||||
date: if friday then sue else jill.
|
||||
|
||||
expensive ||: do_the_math()
|
||||
expensive ||= do_the_math()
|
||||
@@ -2,3 +2,4 @@ hi: `function() {
|
||||
return [document.title, "Hello JavaScript"].join(": ");
|
||||
}`
|
||||
|
||||
|
||||
1
documentation/coffee/expressions_assignment.coffee
Normal file
1
documentation/coffee/expressions_assignment.coffee
Normal file
@@ -0,0 +1 @@
|
||||
six: (one: 1) + (two: 2) + (three: 3)
|
||||
6
documentation/coffee/slices.coffee
Normal file
6
documentation/coffee/slices.coffee
Normal 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]
|
||||
|
||||
@@ -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..."
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@ Animal.prototype.move: meters =>
|
||||
alert(this.name + " moved " + meters + "m.").
|
||||
|
||||
Snake: name => this.name: name.
|
||||
Snake extends new Animal()
|
||||
Snake extends Animal
|
||||
Snake.prototype.move: =>
|
||||
alert("Slithering...")
|
||||
super(5).
|
||||
|
||||
Horse: name => this.name: name.
|
||||
Horse extends new Animal()
|
||||
Horse extends Animal
|
||||
Horse.prototype.move: =>
|
||||
alert("Galloping...")
|
||||
super(45).
|
||||
@@ -1,3 +0,0 @@
|
||||
# CoffeeScript on the left, JS on the right.
|
||||
|
||||
square: x => x * x.
|
||||
@@ -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.
|
||||
@@ -1,2 +0,0 @@
|
||||
nums: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
three_to_six: nums[3, 6]
|
||||
@@ -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;
|
||||
|
||||
@@ -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="#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>&&:</tt>, which only replaces the value of
|
||||
is falsy, and <tt>&&=</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,7 +312,7 @@ 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>.
|
||||
@@ -289,7 +323,7 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
|
||||
</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>
|
||||
@@ -323,14 +357,22 @@ 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="inheritance">
|
||||
<b class="header">Inheritance, and Calling Super from a Subclass</b>
|
||||
@@ -383,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
|
||||
@@ -397,14 +446,15 @@ coffee-script --print app/scripts/*.cs > concatenation.js</pre>
|
||||
</li>
|
||||
<li>
|
||||
Ideas for alternate syntax to end blocks of expressions — 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 — 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>
|
||||
@@ -412,31 +462,73 @@ 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: 20px;">0.1.6</b>
|
||||
Bugfix for running <tt>coffee --interactive</tt> and <tt>--run</tt>
|
||||
from outside of the CoffeeScript directory. Bugfix for nested
|
||||
function/if-statements.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.1.5</b>
|
||||
Array slice literals and array comprehensions can now both take Ruby-style
|
||||
ranges to specify the start and end. JavaScript variable declaration is
|
||||
now pushed up to the top of the scope, making all assignment statements into
|
||||
expressions. You can use <tt>\</tt> to escape newlines.
|
||||
The <tt>coffee-script</tt> command is now called <tt>coffee</tt>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.1.4</b>
|
||||
The official CoffeeScript extension is now <tt>.coffee</tt> instead of
|
||||
<tt>.cs</tt>, which properly belongs to
|
||||
<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
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(function(){
|
||||
var volume;
|
||||
if (ignition === true) {
|
||||
launch();
|
||||
}
|
||||
var volume;
|
||||
if (band !== spinal_tap) {
|
||||
volume = 10;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
})();
|
||||
@@ -1,4 +1,5 @@
|
||||
(function(){
|
||||
var greeting = "Hello CoffeeScript";
|
||||
var difficulty = 0.5;
|
||||
var difficulty, greeting;
|
||||
greeting = "Hello CoffeeScript";
|
||||
difficulty = 0.5;
|
||||
})();
|
||||
@@ -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();
|
||||
})();
|
||||
@@ -1,5 +1,6 @@
|
||||
(function(){
|
||||
var hi = function() {
|
||||
var hi;
|
||||
hi = function() {
|
||||
return [document.title, "Hello JavaScript"].join(": ");
|
||||
};
|
||||
})();
|
||||
@@ -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";
|
||||
})();
|
||||
4
documentation/js/expressions_assignment.js
Normal file
4
documentation/js/expressions_assignment.js
Normal file
@@ -0,0 +1,4 @@
|
||||
(function(){
|
||||
var one, six, three, two;
|
||||
six = (one = 1) + (two = 2) + (three = 3);
|
||||
})();
|
||||
@@ -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;
|
||||
};
|
||||
})();
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
})();
|
||||
@@ -1,9 +1,10 @@
|
||||
(function(){
|
||||
var num = 1;
|
||||
var change_numbers = function() {
|
||||
var change_numbers, new_num, num;
|
||||
num = 1;
|
||||
change_numbers = function() {
|
||||
var new_num;
|
||||
num = 2;
|
||||
var new_num = 3;
|
||||
return new_num;
|
||||
return (new_num = 3);
|
||||
};
|
||||
var new_num = change_numbers();
|
||||
new_num = change_numbers();
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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 \
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
(function(){
|
||||
var Animal = function() {
|
||||
var Animal, Horse, Snake, sam, tom;
|
||||
Animal = function() {
|
||||
};
|
||||
Animal.prototype.move = function(meters) {
|
||||
return alert(this.name + " moved " + meters + "m.");
|
||||
};
|
||||
var Snake = function(name) {
|
||||
this.name = name;
|
||||
return this.name;
|
||||
Snake = function(name) {
|
||||
return (this.name = name);
|
||||
};
|
||||
Snake.prototype.__proto__ = new Animal();
|
||||
Snake.__superClass__ = Animal.prototype;
|
||||
Snake.prototype = new Animal();
|
||||
Snake.prototype.constructor = Snake;
|
||||
Snake.prototype.move = function() {
|
||||
alert("Slithering...");
|
||||
return Snake.prototype.__proto__.move.call(this, 5);
|
||||
return Snake.__superClass__.move.call(this, 5);
|
||||
};
|
||||
var Horse = function(name) {
|
||||
this.name = name;
|
||||
return this.name;
|
||||
Horse = function(name) {
|
||||
return (this.name = name);
|
||||
};
|
||||
Horse.prototype.__proto__ = new Animal();
|
||||
Horse.__superClass__ = Animal.prototype;
|
||||
Horse.prototype = new Animal();
|
||||
Horse.prototype.constructor = Horse;
|
||||
Horse.prototype.move = function() {
|
||||
alert("Galloping...");
|
||||
return Horse.prototype.__proto__.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();
|
||||
})();
|
||||
@@ -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 extends new Animal()
|
||||
Snake extends Animal
|
||||
Snake.prototype.move: =>
|
||||
alert('Slithering...')
|
||||
super(5).
|
||||
|
||||
Horse: name => this.name: name.
|
||||
Horse extends new Animal()
|
||||
Horse extends Animal
|
||||
Horse.prototype.move: =>
|
||||
alert('Galloping...')
|
||||
super(45).
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -93,77 +93,63 @@ _.reduceRight: obj, memo, iterator, context =>
|
||||
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context).
|
||||
results: []
|
||||
_.each(obj, (value, index, list =>
|
||||
iterator.call(context, value, index, list) and results.push(value).))
|
||||
results.push(value) if iterator.call(context, value, index, list).))
|
||||
results.
|
||||
|
||||
#
|
||||
# # Return all the elements for which a truth test fails.
|
||||
# _.reject = function(obj, iterator, context) {
|
||||
# var results = [];
|
||||
# _.each(obj, function(value, index, list) {
|
||||
# !iterator.call(context, value, index, list) && results.push(value);
|
||||
# });
|
||||
# return results;
|
||||
# };
|
||||
#
|
||||
# # Determine whether all of the elements match a truth test. Delegate to
|
||||
# # JavaScript 1.6's every(), if it is present.
|
||||
# _.all = function(obj, iterator, context) {
|
||||
# iterator = iterator || _.identity;
|
||||
# if (obj && _.isFunction(obj.every)) return obj.every(iterator, context);
|
||||
# var result = true;
|
||||
# _.each(obj, function(value, index, list) {
|
||||
# if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
|
||||
# });
|
||||
# return result;
|
||||
# };
|
||||
#
|
||||
# # Determine if at least one element in the object matches a truth test. Use
|
||||
# # JavaScript 1.6's some(), if it exists.
|
||||
# _.any = function(obj, iterator, context) {
|
||||
# iterator = iterator || _.identity;
|
||||
# if (obj && _.isFunction(obj.some)) return obj.some(iterator, context);
|
||||
# var result = false;
|
||||
# _.each(obj, function(value, index, list) {
|
||||
# if (result = iterator.call(context, value, index, list)) _.breakLoop();
|
||||
# });
|
||||
# return result;
|
||||
# };
|
||||
#
|
||||
# # Determine if a given value is included in the array or object,
|
||||
# # based on '==='.
|
||||
# _.include = function(obj, target) {
|
||||
# if (_.isArray(obj)) return _.indexOf(obj, target) != -1;
|
||||
# var found = false;
|
||||
# _.each(obj, function(value) {
|
||||
# if (found = value === target) _.breakLoop();
|
||||
# });
|
||||
# return found;
|
||||
# };
|
||||
#
|
||||
# # Invoke a method with arguments on every item in a collection.
|
||||
# _.invoke = function(obj, method) {
|
||||
# var args = _.rest(arguments, 2);
|
||||
# return _.map(obj, function(value) {
|
||||
# return (method ? value[method] : value).apply(value, args);
|
||||
# });
|
||||
# };
|
||||
#
|
||||
# # Convenience version of a common use case of map: fetching a property.
|
||||
# _.pluck = function(obj, key) {
|
||||
# return _.map(obj, function(value){ return value[key]; });
|
||||
# };
|
||||
#
|
||||
# # Return the maximum item or (item-based computation).
|
||||
# _.max = function(obj, iterator, context) {
|
||||
# if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
||||
# var result = {computed : -Infinity};
|
||||
# _.each(obj, function(value, index, list) {
|
||||
# var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
# computed >= result.computed && (result = {value : value, computed : computed});
|
||||
# });
|
||||
# return result.value;
|
||||
# };
|
||||
# Return all the elements for which a truth test fails.
|
||||
_.reject: obj, iterator, context =>
|
||||
results: []
|
||||
_.each(obj, (value, index, list =>
|
||||
results.push(value) if not iterator.call(context, value, index, list).))
|
||||
results.
|
||||
|
||||
# Determine whether all of the elements match a truth test. Delegate to
|
||||
# JavaScript 1.6's every(), if it is present.
|
||||
_.all: obj, iterator, context =>
|
||||
iterator ||= _.identity
|
||||
return obj.every(iterator, context) if obj and _.isFunction(obj.every)
|
||||
result: true
|
||||
_.each(obj, (value, index, list =>
|
||||
_.breakLoop() unless result: result and iterator.call(context, value, index, list).))
|
||||
result.
|
||||
|
||||
# Determine if at least one element in the object matches a truth test. Use
|
||||
# JavaScript 1.6's some(), if it exists.
|
||||
_.any: obj, iterator, context =>
|
||||
iterator ||= _.identity
|
||||
return obj.some(iterator, context) if obj and _.isFunction(obj.some)
|
||||
result: false
|
||||
_.each(obj, (value, index, list =>
|
||||
_.breakLoop() if (result: iterator.call(context, value, index, list)).))
|
||||
result.
|
||||
|
||||
# Determine if a given value is included in the array or object,
|
||||
# based on '==='.
|
||||
_.include: obj, target =>
|
||||
return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
|
||||
found: false
|
||||
_.each(obj, (value =>
|
||||
_.breakLoop() if (found: value is target).))
|
||||
found.
|
||||
|
||||
# Invoke a method with arguments on every item in a collection.
|
||||
_.invoke: obj, method =>
|
||||
args: _.rest(arguments, 2)
|
||||
_.map(obj, (value =>
|
||||
(if method then value[method] else value.).apply(value, args).)).
|
||||
|
||||
# Convenience version of a common use case of map: fetching a property.
|
||||
_.pluck: obj, key =>
|
||||
_.map(obj, (value => value[key].)).
|
||||
|
||||
# Return the maximum item or (item-based computation).
|
||||
_.max: obj, iterator, context =>
|
||||
return Math.max.apply(Math, obj) if !iterator and _.isArray(obj)
|
||||
result: {computed: -Infinity}
|
||||
_.each(obj, (value, index, list =>
|
||||
computed: if iterator then iterator.call(context, value, index, list) else value.
|
||||
computed >= result.computed and (result: {value: value, computed: computed}).))
|
||||
result.value.
|
||||
#
|
||||
# # Return the minimum element (or element-based computation).
|
||||
# _.min = function(obj, iterator, context) {
|
||||
@@ -320,18 +306,15 @@ _.reduceRight: obj, memo, iterator, context =>
|
||||
# range[idx++] = i;
|
||||
# }
|
||||
# };
|
||||
#
|
||||
# /* ----------------------- Function Functions: -----------------------------*/
|
||||
#
|
||||
# # Create a function bound to a given object (assigning 'this', and arguments,
|
||||
# # optionally). Binding with arguments is also known as 'curry'.
|
||||
# _.bind = function(func, obj) {
|
||||
# var args = _.rest(arguments, 2);
|
||||
# return function() {
|
||||
# return func.apply(obj || root, args.concat(_.toArray(arguments)));
|
||||
# };
|
||||
# };
|
||||
#
|
||||
|
||||
# ----------------------- Function Functions: -----------------------------
|
||||
|
||||
# Create a function bound to a given object (assigning 'this', and arguments,
|
||||
# optionally). Binding with arguments is also known as 'curry'.
|
||||
_.bind: func, obj =>
|
||||
args: _.rest(arguments, 2)
|
||||
=> func.apply(obj or root, args.concat(_.toArray(arguments)))..
|
||||
|
||||
# # Bind all of an object's methods to that object. Useful for ensuring that
|
||||
# # all callbacks defined on an object belong to it.
|
||||
# _.bindAll = function(obj) {
|
||||
@@ -347,36 +330,27 @@ _.reduceRight: obj, memo, iterator, context =>
|
||||
# var args = _.rest(arguments, 2);
|
||||
# return setTimeout(function(){ return func.apply(func, args); }, wait);
|
||||
# };
|
||||
#
|
||||
# # Defers a function, scheduling it to run after the current call stack has
|
||||
# # cleared.
|
||||
# _.defer = function(func) {
|
||||
# return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
|
||||
# };
|
||||
#
|
||||
# # Returns the first function passed as an argument to the second,
|
||||
# # allowing you to adjust arguments, run code before and after, and
|
||||
# # conditionally execute the original function.
|
||||
# _.wrap = function(func, wrapper) {
|
||||
# return function() {
|
||||
# var args = [func].concat(_.toArray(arguments));
|
||||
# return wrapper.apply(wrapper, args);
|
||||
# };
|
||||
# };
|
||||
#
|
||||
# # Returns a function that is the composition of a list of functions, each
|
||||
# # consuming the return value of the function that follows.
|
||||
# _.compose = function() {
|
||||
# var funcs = _.toArray(arguments);
|
||||
# return function() {
|
||||
# var args = _.toArray(arguments);
|
||||
# for (var i=funcs.length-1; i >= 0; i--) {
|
||||
# args = [funcs[i].apply(this, args)];
|
||||
# }
|
||||
# return args[0];
|
||||
# };
|
||||
# };
|
||||
#
|
||||
|
||||
# Defers a function, scheduling it to run after the current call stack has
|
||||
# cleared.
|
||||
_.defer: func =>
|
||||
_.delay.apply(_, [func, 1].concat(_.rest(arguments))).
|
||||
|
||||
# Returns the first function passed as an argument to the second,
|
||||
# allowing you to adjust arguments, run code before and after, and
|
||||
# conditionally execute the original function.
|
||||
_.wrap: func, wrapper =>
|
||||
=> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))..
|
||||
|
||||
# Returns a function that is the composition of a list of functions, each
|
||||
# consuming the return value of the function that follows.
|
||||
_.compose: =>
|
||||
funcs: _.toArray(arguments)
|
||||
=>
|
||||
args: _.toArray(arguments)
|
||||
args: [funcs[i]].apply(this, args) for i in [(funcs.length - 1)..0].
|
||||
args[0]..
|
||||
|
||||
# /* ------------------------- Object Functions: ---------------------------- */
|
||||
#
|
||||
# # Retrieve the names of an object's properties.
|
||||
@@ -408,81 +382,67 @@ _.reduceRight: obj, memo, iterator, context =>
|
||||
# if (_.isArray(obj)) return obj.slice(0);
|
||||
# return _.extend({}, obj);
|
||||
# };
|
||||
#
|
||||
# # Perform a deep comparison to check if two objects are equal.
|
||||
# _.isEqual = function(a, b) {
|
||||
# # Check object identity.
|
||||
# if (a === b) return true;
|
||||
# # Different types?
|
||||
# var atype = typeof(a), btype = typeof(b);
|
||||
# if (atype != btype) return false;
|
||||
# # Basic equality test (watch out for coercions).
|
||||
# if (a == b) return true;
|
||||
# # One is falsy and the other truthy.
|
||||
# if ((!a && b) || (a && !b)) return false;
|
||||
# # One of them implements an isEqual()?
|
||||
# if (a.isEqual) return a.isEqual(b);
|
||||
# # Check dates' integer values.
|
||||
# if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
|
||||
# # Both are NaN?
|
||||
# if (_.isNaN(a) && _.isNaN(b)) return true;
|
||||
# # Compare regular expressions.
|
||||
# if (_.isRegExp(a) && _.isRegExp(b))
|
||||
# return a.source === b.source &&
|
||||
# a.global === b.global &&
|
||||
# a.ignoreCase === b.ignoreCase &&
|
||||
# a.multiline === b.multiline;
|
||||
# # If a is not an object by this point, we can't handle it.
|
||||
# if (atype !== 'object') return false;
|
||||
# # Check for different array lengths before comparing contents.
|
||||
# if (a.length && (a.length !== b.length)) return false;
|
||||
# # Nothing else worked, deep compare the contents.
|
||||
# var aKeys = _.keys(a), bKeys = _.keys(b);
|
||||
# # Different object sizes?
|
||||
# if (aKeys.length != bKeys.length) return false;
|
||||
# # Recursive comparison of contents.
|
||||
# for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
|
||||
# return true;
|
||||
# };
|
||||
#
|
||||
# # Is a given array or object empty?
|
||||
# _.isEmpty = function(obj) {
|
||||
# return _.keys(obj).length == 0;
|
||||
# };
|
||||
#
|
||||
# # Is a given value a DOM element?
|
||||
# _.isElement = function(obj) {
|
||||
# return !!(obj && obj.nodeType == 1);
|
||||
# };
|
||||
#
|
||||
# # Is a given variable an arguments object?
|
||||
# _.isArguments = function(obj) {
|
||||
# return obj && _.isNumber(obj.length) && !_.isArray(obj) && !propertyIsEnumerable.call(obj, 'length');
|
||||
# };
|
||||
#
|
||||
# # Is the given value NaN -- this one is interesting. NaN != NaN, and
|
||||
# # isNaN(undefined) == true, so we make sure it's a number first.
|
||||
# _.isNaN = function(obj) {
|
||||
# return _.isNumber(obj) && isNaN(obj);
|
||||
# };
|
||||
#
|
||||
# # Is a given value equal to null?
|
||||
# _.isNull = function(obj) {
|
||||
# return obj === null;
|
||||
# };
|
||||
#
|
||||
# # Is a given variable undefined?
|
||||
# _.isUndefined = function(obj) {
|
||||
# return typeof obj == 'undefined';
|
||||
# };
|
||||
#
|
||||
# # Invokes interceptor with the obj, and then returns obj.
|
||||
# # The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
|
||||
# _.tap = function(obj, interceptor) {
|
||||
# interceptor(obj);
|
||||
# return obj;
|
||||
# }
|
||||
#
|
||||
|
||||
# Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual: a, b =>
|
||||
# Check object identity.
|
||||
return true if a is b
|
||||
# Different types?
|
||||
atype: typeof(a); btype: typeof(b)
|
||||
return false if atype isnt btype
|
||||
# Basic equality test (watch out for coercions).
|
||||
return true if `a == b`
|
||||
# One is falsy and the other truthy.
|
||||
return false if (!a and b) or (a and !b)
|
||||
# One of them implements an isEqual()?
|
||||
return a.isEqual(b) if a.isEqual
|
||||
# Check dates' integer values.
|
||||
return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
|
||||
# Both are NaN?
|
||||
return true if _.isNaN(a) and _.isNaN(b)
|
||||
# Compare regular expressions.
|
||||
if _.isRegExp(a) and _.isRegExp(b)
|
||||
return a.source is b.source and \
|
||||
a.global is b.global and \
|
||||
a.ignoreCase is b.ignoreCase and \
|
||||
a.multiline is b.multiline.
|
||||
# If a is not an object by this point, we can't handle it.
|
||||
return false if atype isnt 'object'
|
||||
# Check for different array lengths before comparing contents.
|
||||
return false if a.length and (a.length isnt b.length)
|
||||
# Nothing else worked, deep compare the contents.
|
||||
aKeys: _.keys(a); bKeys: _.keys(b)
|
||||
# Different object sizes?
|
||||
return false if aKeys.length isnt bKeys.length
|
||||
# Recursive comparison of contents.
|
||||
# for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
|
||||
return true.
|
||||
|
||||
# Is a given array or object empty?
|
||||
_.isEmpty: obj => _.keys(obj).length is 0.
|
||||
|
||||
# Is a given value a DOM element?
|
||||
_.isElement: obj => !!(obj and obj.nodeType is 1).
|
||||
|
||||
# Is a given variable an arguments object?
|
||||
_.isArguments: obj => obj and _.isNumber(obj.length) and !_.isArray(obj) and !propertyIsEnumerable.call(obj, 'length').
|
||||
|
||||
# Is the given value NaN -- this one is interesting. NaN != NaN, and
|
||||
# isNaN(undefined) == true, so we make sure it's a number first.
|
||||
_.isNaN: obj => _.isNumber(obj) and isNaN(obj).
|
||||
|
||||
# Is a given value equal to null?
|
||||
_.isNull: obj => obj is null.
|
||||
|
||||
# Is a given variable undefined?
|
||||
_.isUndefined: obj => typeof obj is 'undefined'.
|
||||
|
||||
# Invokes interceptor with the obj, and then returns obj.
|
||||
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
|
||||
_.tap: obj, interceptor =>
|
||||
interceptor(obj)
|
||||
obj.
|
||||
|
||||
# # Define the isArray, isDate, isFunction, isNumber, isRegExp, and isString
|
||||
# # functions based on their toString identifiers.
|
||||
# var types = ['Array', 'Date', 'Function', 'Number', 'RegExp', 'String'];
|
||||
@@ -492,26 +452,21 @@ _.reduceRight: obj, memo, iterator, context =>
|
||||
# _['is' + types[i]] = function(obj) { return toString.call(obj) == identifier; };
|
||||
# })();
|
||||
# }
|
||||
#
|
||||
# /* -------------------------- Utility Functions: -------------------------- */
|
||||
#
|
||||
# # Run Underscore.js in noConflict mode, returning the '_' variable to its
|
||||
# # previous owner. Returns a reference to the Underscore object.
|
||||
# _.noConflict = function() {
|
||||
# root._ = previousUnderscore;
|
||||
# return this;
|
||||
# };
|
||||
#
|
||||
# # Keep the identity function around for default iterators.
|
||||
# _.identity = function(value) {
|
||||
# return value;
|
||||
# };
|
||||
#
|
||||
# # Break out of the middle of an iteration.
|
||||
# _.breakLoop = function() {
|
||||
# throw breaker;
|
||||
# };
|
||||
#
|
||||
|
||||
# -------------------------- Utility Functions: --------------------------
|
||||
|
||||
# Run Underscore.js in noConflict mode, returning the '_' variable to its
|
||||
# previous owner. Returns a reference to the Underscore object.
|
||||
_.noConflict: =>
|
||||
root._: previousUnderscore
|
||||
this.
|
||||
|
||||
# Keep the identity function around for default iterators.
|
||||
_.identity: value => value.
|
||||
|
||||
# Break out of the middle of an iteration.
|
||||
_.breakLoop: => throw breaker.
|
||||
|
||||
# # Generate a unique integer id (unique within the entire client session).
|
||||
# # Useful for temporary DOM ids.
|
||||
# var idCounter = 0;
|
||||
@@ -537,19 +492,19 @@ _.reduceRight: obj, memo, iterator, context =>
|
||||
# + "');}return p.join('');");
|
||||
# return data ? fn(data) : fn;
|
||||
# };
|
||||
#
|
||||
# /*------------------------------- Aliases ----------------------------------*/
|
||||
#
|
||||
# _.forEach = _.each;
|
||||
# _.foldl = _.inject = _.reduce;
|
||||
# _.foldr = _.reduceRight;
|
||||
# _.filter = _.select;
|
||||
# _.every = _.all;
|
||||
# _.some = _.any;
|
||||
# _.head = _.first;
|
||||
# _.tail = _.rest;
|
||||
# _.methods = _.functions;
|
||||
#
|
||||
|
||||
# ------------------------------- Aliases ----------------------------------
|
||||
|
||||
_.forEach: _.each
|
||||
_.foldl: _.inject: _.reduce
|
||||
_.foldr: _.reduceRight
|
||||
_.filter: _.select
|
||||
_.every: _.all
|
||||
_.some: _.any
|
||||
_.head: _.first
|
||||
_.tail: _.rest
|
||||
_.methods: _.functions
|
||||
|
||||
# /*------------------------ Setup the OOP Wrapper: --------------------------*/
|
||||
#
|
||||
# # Helper function to continue chaining intermediate results.
|
||||
424
index.html
424
index.html
@@ -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="#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"><</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"><</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">=></span> x <span class="Keyword">*</span> x.
|
||||
<span class="FunctionName">cube</span><span class="Keyword">:</span> <span class="FunctionArgument">x</span> <span class="Storage">=></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">"</span>Hello CoffeeScript<span class="String">"</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">"</span>Hello CoffeeScript<span class="String">"</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">"</span>Hello CoffeeScript<span class="String">"</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">"</span>do<span class="String">"</span></span>, <span class="String"><span class="String">"</span>re<span class="String">"</span></span>, <span class="String"><span class="String">"</span>mi<span class="String">"</span></span>, <span class="String"><span class="String">"</span>fa<span class="String">"</span></span>, <span class="String"><span class="String">"</span>so<span class="String">"</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">"</span>do<span class="String">"</span></span>, <span class="String"><span class="String">"</span>re<span class="String">"</span></span>, <span class="String"><span class="String">"</span>mi<span class="String">"</span></span>, <span class="String"><span class="String">"</span>fa<span class="String">"</span></span>, <span class="String"><span class="String">"</span>so<span class="String">"</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,22 @@ var ages = {
|
||||
num<span class="Keyword">:</span> <span class="Number">2</span>
|
||||
new_num<span class="Keyword">:</span> <span class="Number">3</span>.
|
||||
new_num<span class="Keyword">:</span> change_numbers()
|
||||
</pre><pre class="idle"><span class="Storage">var</span> num <span class="Keyword">=</span> <span class="Number">1</span>;
|
||||
<span class="Storage">var</span> <span class="FunctionName">change_numbers</span> = <span class="Storage">function</span>() {
|
||||
</pre><pre class="idle"><span class="Storage">var</span> change_numbers, new_num, num;
|
||||
num <span class="Keyword">=</span> <span class="Number">1</span>;
|
||||
<span class="FunctionName">change_numbers</span> = <span class="Storage">function</span>() {
|
||||
<span class="Storage">var</span> new_num;
|
||||
num <span class="Keyword">=</span> <span class="Number">2</span>;
|
||||
<span class="Storage">var</span> new_num <span class="Keyword">=</span> <span class="Number">3</span>;
|
||||
<span class="Keyword">return</span> new_num;
|
||||
<span class="Keyword">return</span> (new_num <span class="Keyword">=</span> <span class="Number">3</span>);
|
||||
};
|
||||
<span class="Storage">var</span> new_num <span class="Keyword">=</span> change_numbers();
|
||||
</pre><button onclick='javascript: var num = 1;
|
||||
var change_numbers = function() {
|
||||
new_num <span class="Keyword">=</span> change_numbers();
|
||||
</pre><button onclick='javascript: var change_numbers, new_num, num;
|
||||
num = 1;
|
||||
change_numbers = function() {
|
||||
var new_num;
|
||||
num = 2;
|
||||
var new_num = 3;
|
||||
return new_num;
|
||||
return (new_num = 3);
|
||||
};
|
||||
var new_num = change_numbers();
|
||||
new_num = change_numbers();
|
||||
;alert(new_num);'>run: new_num</button><br class='clear' /></div>
|
||||
<p>
|
||||
Notice how the variables are declared with <tt>var</tt> the first time
|
||||
@@ -383,8 +418,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 +427,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>&&:</tt>, which only replaces the value of
|
||||
is falsy, and <tt>&&=</tt>, which only replaces the value of
|
||||
truthy variables.
|
||||
</p>
|
||||
|
||||
@@ -420,7 +455,8 @@ expensive <span class="Keyword">=</span> expensive <span class="Keyword">||</spa
|
||||
<span class="String"><span class="String">"</span>C<span class="String">"</span></span>..
|
||||
|
||||
eldest<span class="Keyword">:</span> <span class="Keyword">if</span> <span class="Number">24</span> <span class="Keyword">></span> <span class="Number">21</span> <span class="Keyword">then</span> <span class="String"><span class="String">"</span>Liz<span class="String">"</span></span> <span class="Keyword">else</span> <span class="String"><span class="String">"</span>Ike<span class="String">"</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">"</span>A+<span class="String">"</span></span>;
|
||||
} <span class="Keyword">else</span> <span class="Keyword">if</span> (student.okay_stuff) {
|
||||
@@ -429,8 +465,9 @@ eldest<span class="Keyword">:</span> <span class="Keyword">if</span> <span class
|
||||
<span class="Keyword">return</span> <span class="String"><span class="String">"</span>C<span class="String">"</span></span>;
|
||||
}
|
||||
};
|
||||
<span class="Storage">var</span> eldest <span class="Keyword">=</span> <span class="Number">24</span> <span class="Keyword">></span> <span class="Number">21</span> ? <span class="String"><span class="String">"</span>Liz<span class="String">"</span></span> : <span class="String"><span class="String">"</span>Ike<span class="String">"</span></span>;
|
||||
</pre><button onclick='javascript: var grade = function(student) {
|
||||
eldest <span class="Keyword">=</span> <span class="Number">24</span> <span class="Keyword">></span> <span class="Number">21</span> ? <span class="String"><span class="String">"</span>Liz<span class="String">"</span></span> : <span class="String"><span class="String">"</span>Ike<span class="String">"</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 +476,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,7 +500,7 @@ 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>.
|
||||
@@ -464,7 +511,7 @@ var eldest = 24 > 21 ? "Liz" : "Ike";
|
||||
</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>
|
||||
@@ -476,15 +523,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"><</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>;
|
||||
}
|
||||
@@ -532,38 +579,55 @@ lunch<span class="Keyword">:</span> food.eat() <span class="Keyword">for</span>
|
||||
|
||||
<span class="Comment"><span class="Comment">#</span> Zebra-stripe a table.</span>
|
||||
highlight(row) <span class="Keyword">for</span> row, i <span class="Keyword">in</span> table <span class="Keyword">if</span> i <span class="Keyword">%</span> <span class="Number">2</span> <span class="Keyword">is</span> <span class="Number">0</span>.
|
||||
</pre><pre class="idle">
|
||||
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, __d, __e, __f, __g, __h, food, i, lunch, row;
|
||||
<span class="Comment"><span class="Comment">//</span> Eat lunch.</span>
|
||||
<span class="Storage">var</span> lunch;
|
||||
<span class="Storage">var</span> a <span class="Keyword">=</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>];
|
||||
<span class="Storage">var</span> d <span class="Keyword">=</span> [];
|
||||
<span class="Keyword">for</span> (<span class="Storage">var</span> b<span class="Keyword">=</span><span class="Number">0</span>, c<span class="Keyword">=</span>a.<span class="LibraryConstant">length</span>; b<span class="Keyword"><</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"><</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"><</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"><</span>__g; __f<span class="Keyword">++</span>) {
|
||||
row <span class="Keyword">=</span> __e[__f];
|
||||
i <span class="Keyword">=</span> __f;
|
||||
__h[__f] <span class="Keyword">=</span> i <span class="Keyword">%</span> <span class="Number">2</span> <span class="Keyword">===</span> <span class="Number">0</span> ? highlight(row) : <span class="BuiltInConstant">null</span>;
|
||||
}
|
||||
__h;
|
||||
</pre><br class='clear' /></div>
|
||||
<p>
|
||||
If you're not iterating over an actual array, you can use a range to
|
||||
specify the start and end of an array comprehension:
|
||||
<tt>coundown(i) for i in [10..1].</tt>
|
||||
</p>
|
||||
|
||||
<p id="slice">
|
||||
<b class="header">Array Slice Literals</b>
|
||||
CoffeeScript includes syntax for extracting slices of arrays.
|
||||
The first argument is the index of the first element in the slice, and
|
||||
the second is the index of the last one.
|
||||
<b class="header">Slicing Arrays with Ranges</b>
|
||||
CoffeeScript borrows Ruby's
|
||||
<a href="http://ruby-doc.org/core/classes/Range.html">range syntax</a>
|
||||
for extracting slices of arrays. With two dots (<tt>3..5</tt>), the range
|
||||
is inclusive: the first argument is the index of the first element in
|
||||
the slice, and the second is the index of the last one. Three dots signify
|
||||
a range that excludes the end.
|
||||
</p>
|
||||
<div class='code'><pre class="idle">nums<span class="Keyword">:</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>]
|
||||
three_to_six<span class="Keyword">:</span> nums[<span class="Number">3</span>, <span class="Number">6</span>]
|
||||
</pre><pre class="idle"><span class="Storage">var</span> nums <span class="Keyword">=</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>];
|
||||
<span class="Storage">var</span> three_to_six <span class="Keyword">=</span> nums.<span class="LibraryFunction">slice</span>(<span class="Number">3</span>, <span class="Number">6</span> <span class="Keyword">+</span> <span class="Number">1</span>);
|
||||
</pre><button onclick='javascript: var nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
var three_to_six = nums.slice(3, 6 + 1);
|
||||
;alert(three_to_six);'>run: three_to_six</button><br class='clear' /></div>
|
||||
<div class='code'><pre class="idle">numbers<span class="Keyword">:</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>]
|
||||
|
||||
three_to_six<span class="Keyword">:</span> numbers[<span class="Number">3</span>..<span class="Number">6</span>]
|
||||
|
||||
numbers_copy<span class="Keyword">:</span> numbers[<span class="Number">0</span>...numbers.length]
|
||||
|
||||
</pre><pre class="idle"><span class="Storage">var</span> numbers, numbers_copy, three_to_six;
|
||||
numbers <span class="Keyword">=</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>];
|
||||
three_to_six <span class="Keyword">=</span> numbers.<span class="LibraryFunction">slice</span>(<span class="Number">3</span>, <span class="Number">6</span> <span class="Keyword">+</span> <span class="Number">1</span>);
|
||||
numbers_copy <span class="Keyword">=</span> numbers.<span class="LibraryFunction">slice</span>(<span class="Number">0</span>, numbers.<span class="LibraryConstant">length</span>);
|
||||
</pre><button onclick='javascript: var numbers, numbers_copy, three_to_six;
|
||||
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
three_to_six = numbers.slice(3, 6 + 1);
|
||||
numbers_copy = numbers.slice(0, numbers.length);
|
||||
;alert(numbers_copy);'>run: numbers_copy</button><br class='clear' /></div>
|
||||
|
||||
<p id="inheritance">
|
||||
<b class="header">Inheritance, and Calling Super from a Subclass</b>
|
||||
@@ -587,13 +651,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">"</span> moved <span class="String">"</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">"</span>m.<span class="String">"</span></span>).
|
||||
|
||||
<span class="FunctionName">Snake</span><span class="Keyword">:</span> <span class="FunctionArgument">name</span> <span class="Storage">=></span> <span class="Variable">this</span>.name<span class="Keyword">:</span> name.
|
||||
Snake <span class="Variable">extends</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">=></span>
|
||||
alert(<span class="String"><span class="String">"</span>Slithering...<span class="String">"</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">=></span> <span class="Variable">this</span>.name<span class="Keyword">:</span> name.
|
||||
Horse <span class="Variable">extends</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">=></span>
|
||||
alert(<span class="String"><span class="String">"</span>Galloping...<span class="String">"</span></span>)
|
||||
<span class="Variable">super</span>(<span class="Number">45</span>).
|
||||
@@ -607,58 +671,64 @@ 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">"</span> moved <span class="String">"</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">"</span>m.<span class="String">"</span></span>);
|
||||
};
|
||||
<span class="Storage">var</span> <span class="FunctionName">Snake</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
|
||||
<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name;
|
||||
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>;
|
||||
<span class="FunctionName">Snake</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
|
||||
<span class="Keyword">return</span> (<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name);
|
||||
};
|
||||
<span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">__proto__</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
|
||||
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">"</span>Slithering...<span class="String">"</span></span>);
|
||||
<span class="Keyword">return</span> Snake.<span class="LibraryConstant">prototype</span>.__proto__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">5</span>);
|
||||
<span class="Keyword">return</span> Snake.__superClass__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">5</span>);
|
||||
};
|
||||
<span class="Storage">var</span> <span class="FunctionName">Horse</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
|
||||
<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name;
|
||||
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>;
|
||||
<span class="FunctionName">Horse</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
|
||||
<span class="Keyword">return</span> (<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name);
|
||||
};
|
||||
<span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span>.<span class="FunctionName">__proto__</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
|
||||
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">"</span>Galloping...<span class="String">"</span></span>);
|
||||
<span class="Keyword">return</span> Horse.<span class="LibraryConstant">prototype</span>.__proto__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">45</span>);
|
||||
<span class="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">"</span>Sammy the Python<span class="String">"</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">"</span>Tommy the Palomino<span class="String">"</span></span>);
|
||||
sam <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Snake</span>(<span class="String"><span class="String">"</span>Sammy the Python<span class="String">"</span></span>);
|
||||
tom <span class="Keyword">=</span> <span class="Keyword">new</span> <span class="TypeName">Horse</span>(<span class="String"><span class="String">"</span>Tommy the Palomino<span class="String">"</span></span>);
|
||||
sam.move();
|
||||
tom.move();
|
||||
</pre><button onclick='javascript: var Animal = function() {
|
||||
</pre><button onclick='javascript: var Animal, Horse, Snake, sam, tom;
|
||||
Animal = function() {
|
||||
};
|
||||
Animal.prototype.move = function(meters) {
|
||||
return alert(this.name + " moved " + meters + "m.");
|
||||
};
|
||||
var Snake = function(name) {
|
||||
this.name = name;
|
||||
return this.name;
|
||||
Snake = function(name) {
|
||||
return (this.name = name);
|
||||
};
|
||||
Snake.prototype.__proto__ = new Animal();
|
||||
Snake.__superClass__ = Animal.prototype;
|
||||
Snake.prototype = new Animal();
|
||||
Snake.prototype.constructor = Snake;
|
||||
Snake.prototype.move = function() {
|
||||
alert("Slithering...");
|
||||
return Snake.prototype.__proto__.move.call(this, 5);
|
||||
return Snake.__superClass__.move.call(this, 5);
|
||||
};
|
||||
var Horse = function(name) {
|
||||
this.name = name;
|
||||
return this.name;
|
||||
Horse = function(name) {
|
||||
return (this.name = name);
|
||||
};
|
||||
Horse.prototype.__proto__ = new Animal();
|
||||
Horse.__superClass__ = Animal.prototype;
|
||||
Horse.prototype = new Animal();
|
||||
Horse.prototype.constructor = Horse;
|
||||
Horse.prototype.move = function() {
|
||||
alert("Galloping...");
|
||||
return Horse.prototype.__proto__.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>
|
||||
@@ -672,10 +742,13 @@ tom.move();
|
||||
<span class="String"> return [document.title, "Hello JavaScript"].join(": ");</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">"</span>Hello JavaScript<span class="String">"</span></span>].<span class="LibraryFunction">join</span>(<span class="String"><span class="String">"</span>: <span class="String">"</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>
|
||||
@@ -748,13 +821,16 @@ when <span class="String"><span class="String">"</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">"</span></span>
|
||||
|
||||
</pre><pre class="idle"><span class="Storage">var</span> moby_dick <span class="Keyword">=</span> <span class="String"><span class="String">"</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">"</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">"</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 \
|
||||
@@ -762,13 +838,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
|
||||
@@ -776,14 +859,15 @@ world...";
|
||||
</li>
|
||||
<li>
|
||||
Ideas for alternate syntax to end blocks of expressions — 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 — 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>
|
||||
@@ -791,31 +875,73 @@ 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: 20px;">0.1.6</b>
|
||||
Bugfix for running <tt>coffee --interactive</tt> and <tt>--run</tt>
|
||||
from outside of the CoffeeScript directory. Bugfix for nested
|
||||
function/if-statements.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.1.5</b>
|
||||
Array slice literals and array comprehensions can now both take Ruby-style
|
||||
ranges to specify the start and end. JavaScript variable declaration is
|
||||
now pushed up to the top of the scope, making all assignment statements into
|
||||
expressions. You can use <tt>\</tt> to escape newlines.
|
||||
The <tt>coffee-script</tt> command is now called <tt>coffee</tt>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.1.4</b>
|
||||
The official CoffeeScript extension is now <tt>.coffee</tt> instead of
|
||||
<tt>.cs</tt>, which properly belongs to
|
||||
<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
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
var FILE = require("file");
|
||||
var OS = require("os");
|
||||
|
||||
exports.run = function(args) {
|
||||
// TODO: non-REPL
|
||||
|
||||
args.shift();
|
||||
|
||||
if (args.length) {
|
||||
require(FILE.absolute(args[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
try {
|
||||
system.stdout.write("cs> ").flush();
|
||||
|
||||
var result = exports.cs_eval(require("readline").readline());
|
||||
|
||||
if (result !== undefined)
|
||||
print(result);
|
||||
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// executes the coffee-script Ruby program to convert from CoffeeScript to Objective-J.
|
||||
// eventually this will hopefully be replaced by a JavaScript program.
|
||||
var coffeePath = FILE.path(module.path).dirname().dirname().join("bin", "coffee-script");
|
||||
|
||||
exports.compileFile = function(path) {
|
||||
var coffee = OS.popen([coffeePath, "--print", path]);
|
||||
|
||||
if (coffee.wait() !== 0)
|
||||
throw new Error("coffee compiler error");
|
||||
|
||||
return coffee.stdout.read();
|
||||
}
|
||||
|
||||
exports.compile = function(source) {
|
||||
var coffee = OS.popen([coffeePath, "-e"]);
|
||||
|
||||
coffee.stdin.write(source).flush().close();
|
||||
|
||||
if (coffee.wait() !== 0)
|
||||
throw new Error("coffee compiler error");
|
||||
|
||||
return coffee.stdout.read();
|
||||
}
|
||||
|
||||
// these two functions are equivalent to objective-j's objj_eval/make_narwhal_factory.
|
||||
// implemented as a call to coffee and objj_eval/make_narwhal_factory
|
||||
exports.cs_eval = function(source) {
|
||||
init();
|
||||
|
||||
var code = exports.compile(source);
|
||||
|
||||
// strip the function wrapper, we add our own.
|
||||
// TODO: this is very fragile
|
||||
code = code.split("\n").slice(1,-2).join("\n");
|
||||
|
||||
return eval(code);
|
||||
}
|
||||
|
||||
exports.make_narwhal_factory = function(path) {
|
||||
init();
|
||||
|
||||
var code = exports.compileFile(path);
|
||||
|
||||
// strip the function wrapper, we add our own.
|
||||
// TODO: this is very fragile
|
||||
code = code.split("\n").slice(1,-2).join("\n");
|
||||
|
||||
var factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
|
||||
|
||||
if (system.engine === "rhino")
|
||||
return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
|
||||
|
||||
// eval requires parenthesis, but parenthesis break compileFunction.
|
||||
else
|
||||
return eval("(" + factoryText + ")");
|
||||
}
|
||||
|
||||
|
||||
var init = function() {
|
||||
// make sure it's only done once
|
||||
init = function(){}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
var coffeescript = null;
|
||||
|
||||
function CoffeeScriptLoader() {
|
||||
var loader = {};
|
||||
var factories = {};
|
||||
|
||||
loader.reload = function(topId, path) {
|
||||
if (!coffeescript) coffeescript = require("coffee-script");
|
||||
|
||||
//print("loading objective-j: " + topId + " (" + path + ")");
|
||||
factories[topId] = coffeescript.make_narwhal_factory(path);
|
||||
}
|
||||
|
||||
loader.load = function(topId, path) {
|
||||
if (!factories.hasOwnProperty(topId))
|
||||
loader.reload(topId, path);
|
||||
return factories[topId];
|
||||
}
|
||||
|
||||
return loader;
|
||||
};
|
||||
|
||||
require.loader.loaders.unshift([".cs", CoffeeScriptLoader()]);
|
||||
@@ -9,7 +9,7 @@ require "coffee_script/parse_error"
|
||||
# Namespace for all CoffeeScript internal classes.
|
||||
module CoffeeScript
|
||||
|
||||
VERSION = '0.1.2' # Keep in sync with the gemspec.
|
||||
VERSION = '0.1.6' # Keep in sync with the gemspec.
|
||||
|
||||
# Compile a script (String or IO) to JavaScript.
|
||||
def self.compile(script, options={})
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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*(=>)</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*(=>)</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|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>!|\$|%|&|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|aint|not)\b</string>
|
||||
<string>!|\$|%|&|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\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>
|
||||
|
||||
@@ -5,26 +5,31 @@ 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.
|
||||
WATCH_INTERVAL = 0.5
|
||||
|
||||
# Path to the Narwhal Launcher:
|
||||
LAUNCHER = File.expand_path(File.dirname(__FILE__)) + '/narwhal/js/launcher.js'
|
||||
|
||||
# Run the CommandLine off the contents of ARGV.
|
||||
def initialize
|
||||
@mtimes = {}
|
||||
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 +105,36 @@ Usage:
|
||||
puts js
|
||||
end
|
||||
|
||||
# Use Narwhal to run an interactive CoffeeScript session.
|
||||
def launch_repl
|
||||
exec "narwhal #{LAUNCHER}"
|
||||
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 #{LAUNCHER} #{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, :no_wrap => @options[:no_wrap])
|
||||
rescue CoffeeScript::ParseError => e
|
||||
STDERR.puts e.message(source)
|
||||
options = {}
|
||||
options[:no_wrap] = true if @options[:no_wrap]
|
||||
CoffeeScript.compile(script, options)
|
||||
rescue CoffeeScript::ParseError, SyntaxError => e
|
||||
STDERR.puts "#{source}: #{e.message}"
|
||||
exit(1) unless @options[:watch]
|
||||
nil
|
||||
end
|
||||
@@ -134,6 +158,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 +177,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 or read from stdin') 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,15 +186,15 @@ Usage:
|
||||
opts.on('-v', '--verbose', 'print at every step of code generation') do |v|
|
||||
ENV['VERBOSE'] = 'true'
|
||||
end
|
||||
opts.on('-n', '--no-wrap', 'suppress the top-level safety function wrapper') do |n|
|
||||
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
|
||||
|
||||
@@ -24,20 +24,20 @@ prechigh
|
||||
left '<<' '>>' '>>>'
|
||||
left '&' '|' '^'
|
||||
left '<=' '<' '>' '>='
|
||||
right '==' '!=' IS AINT
|
||||
right '==' '!=' IS ISNT
|
||||
left '&&' '||' AND OR
|
||||
right '-=' '+=' '/=' '*='
|
||||
right '-=' '+=' '/=' '*=' '%='
|
||||
right DELETE INSTANCEOF TYPEOF
|
||||
left "."
|
||||
left '.'
|
||||
right THROW FOR IN WHILE NEW SUPER
|
||||
left UNLESS IF ELSE EXTENDS
|
||||
left ":" '||:' '&&:'
|
||||
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.
|
||||
@@ -115,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] }
|
||||
;
|
||||
|
||||
@@ -143,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]) }
|
||||
@@ -171,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]) }
|
||||
@@ -182,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]) }
|
||||
@@ -211,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]) }
|
||||
@@ -222,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.
|
||||
@@ -230,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]) }
|
||||
@@ -257,12 +255,13 @@ rule
|
||||
|
||||
# Extending an object's prototype.
|
||||
Extends:
|
||||
Value EXTENDS Expression { result = ExtendsNode.new(val[0], val[2]) }
|
||||
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.
|
||||
@@ -270,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]) }
|
||||
@@ -313,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.
|
||||
|
||||
@@ -8,7 +8,7 @@ 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",
|
||||
@@ -19,7 +19,7 @@ module CoffeeScript
|
||||
|
||||
# Token matching regexes.
|
||||
IDENTIFIER = /\A([a-zA-Z$_]\w*)/
|
||||
NUMBER = /\A\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?))\b/i
|
||||
NUMBER = /\A((\b|-)((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
|
||||
STRING = /\A(""|''|"(.*?)[^\\]"|'(.*?)[^\\]')/m
|
||||
JS = /\A(``|`(.*?)[^\\]`)/m
|
||||
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
|
||||
@@ -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
|
||||
|
||||
|
||||
61
lib/coffee_script/narwhal/coffee-script.coffee
Normal file
61
lib/coffee_script/narwhal/coffee-script.coffee
Normal file
@@ -0,0 +1,61 @@
|
||||
# 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()
|
||||
if args.length
|
||||
exports.evalCS(File.read(path)) for path in args.
|
||||
return true.
|
||||
|
||||
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){ 1 + 1 /**/\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 + ")")..
|
||||
76
lib/coffee_script/narwhal/js/coffee-script.js
Normal file
76
lib/coffee_script/narwhal/js/coffee-script.js
Normal file
@@ -0,0 +1,76 @@
|
||||
(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 __a, __b, __c, __d, path, result;
|
||||
args.shift();
|
||||
if (args.length) {
|
||||
__a = args;
|
||||
__d = [];
|
||||
for (__b=0, __c=__a.length; __b<__c; __b++) {
|
||||
path = __a[__b];
|
||||
__d[__b] = exports.evalCS(File.read(path));
|
||||
}
|
||||
__d;
|
||||
return true;
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
system.stdout.write('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){ 1 + 1 /**/\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 + ")");
|
||||
}
|
||||
};
|
||||
})();
|
||||
3
lib/coffee_script/narwhal/js/launcher.js
Normal file
3
lib/coffee_script/narwhal/js/launcher.js
Normal file
@@ -0,0 +1,3 @@
|
||||
(function(){
|
||||
require("./coffee-script").run(system.args);
|
||||
})();
|
||||
19
lib/coffee_script/narwhal/js/loader.js
Normal file
19
lib/coffee_script/narwhal/js/loader.js
Normal file
@@ -0,0 +1,19 @@
|
||||
(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');
|
||||
return (factories[topId] = coffeescript.makeNarwhalFactory(path));
|
||||
},
|
||||
// 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]);
|
||||
})();
|
||||
1
lib/coffee_script/narwhal/launcher.coffee
Normal file
1
lib/coffee_script/narwhal/launcher.coffee
Normal file
@@ -0,0 +1 @@
|
||||
require("./coffee-script").run(system.args)
|
||||
19
lib/coffee_script/narwhal/loader.coffee
Normal file
19
lib/coffee_script/narwhal/loader.coffee
Normal 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])
|
||||
@@ -77,36 +77,42 @@ module CoffeeScript
|
||||
# If this is the top-level Expressions, wrap everything in a safety closure.
|
||||
def root_compile(o={})
|
||||
indent = o[:no_wrap] ? '' : TAB
|
||||
code = compile(:indent => indent, :scope => Scope.new)
|
||||
code = compile(o.merge(:indent => indent, :scope => Scope.new), o[:no_wrap] ? nil : :code)
|
||||
code.gsub!(STRIP_TRAILING_WHITESPACE, '')
|
||||
o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
|
||||
end
|
||||
|
||||
# The extra fancy is to handle pushing down returns and assignments
|
||||
# recursively to the final lines of inner statements.
|
||||
def compile(options={})
|
||||
# Variables first defined within the Expressions body have their
|
||||
# declarations pushed up to the top scope.
|
||||
def compile(options={}, parent=nil)
|
||||
return root_compile(options) unless options[:scope]
|
||||
code = @expressions.map { |node|
|
||||
compiled = @expressions.map do |node|
|
||||
o = super(options)
|
||||
if last?(node) && (o[:return] || o[:assign])
|
||||
if o[:return]
|
||||
if node.statement? || node.custom_return?
|
||||
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
|
||||
else
|
||||
o.delete(:return)
|
||||
"#{o[:indent]}return #{node.compile(o)}#{node.line_ending}"
|
||||
end
|
||||
elsif o[:assign]
|
||||
if node.statement? || node.custom_assign?
|
||||
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
|
||||
else
|
||||
"#{o[:indent]}#{AssignNode.new(ValueNode.new(LiteralNode.new(o[:assign])), node).compile(o)};"
|
||||
"#{o[:indent]}#{AssignNode.new(o[:assign], node).compile(o)};"
|
||||
end
|
||||
end
|
||||
else
|
||||
o.delete(:return) and o.delete(:assign)
|
||||
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
|
||||
end
|
||||
}.join("\n")
|
||||
end
|
||||
scope = options[:scope]
|
||||
declarations = scope.any_declared? && parent == :code ? "#{options[:indent]}var #{scope.declared_variables.join(', ')};\n" : ''
|
||||
code = declarations + compiled.join("\n")
|
||||
write(code)
|
||||
end
|
||||
end
|
||||
@@ -202,7 +208,7 @@ module CoffeeScript
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
args = @arguments.map{|a| a.compile(o.merge(:no_paren => true)) }.join(', ')
|
||||
args = @arguments.map{|a| a.compile(o) }.join(', ')
|
||||
return write(compile_super(args, o)) if super?
|
||||
prefix = @new ? "new " : ''
|
||||
write("#{prefix}#{@variable.compile(o)}(#{args})")
|
||||
@@ -211,11 +217,12 @@ module CoffeeScript
|
||||
def compile_super(args, o)
|
||||
methname = o[:last_assign].sub(LEADING_DOT, '')
|
||||
arg_part = args.empty? ? '' : ", #{args}"
|
||||
"#{o[:proto_assign]}.prototype.__proto__.#{methname}.call(this#{arg_part})"
|
||||
"#{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
|
||||
|
||||
@@ -224,7 +231,10 @@ module CoffeeScript
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
"#{@sub_object.compile(o)}.prototype.__proto__ = #{@super_object.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
|
||||
@@ -296,28 +306,60 @@ module CoffeeScript
|
||||
end
|
||||
end
|
||||
|
||||
# A range literal. Ranges can be used to extract portions (slices) of arrays,
|
||||
# or to specify a range for array comprehensions.
|
||||
class RangeNode
|
||||
attr_reader :from, :to
|
||||
|
||||
def initialize(from, to, exclusive=false)
|
||||
@from, @to, @exclusive = from, to, exclusive
|
||||
end
|
||||
|
||||
def exclusive?
|
||||
@exclusive
|
||||
end
|
||||
|
||||
def less_operator
|
||||
@exclusive ? '<' : '<='
|
||||
end
|
||||
|
||||
def greater_operator
|
||||
@exclusive ? '>' : '>='
|
||||
end
|
||||
|
||||
def compile(o, fv, tv)
|
||||
fvv, tvv = @from.compile(o), @to.compile(o)
|
||||
vars = "#{fv}=#{fvv}, #{tv}=#{tvv}"
|
||||
compare = "(#{fvv} <= #{tvv} ? #{fv} #{less_operator} #{tv} : #{fv} #{greater_operator} #{tv})"
|
||||
incr = "(#{fvv} <= #{tvv} ? #{fv} += 1 : #{fv} -= 1)"
|
||||
"#{vars}; #{compare}; #{incr}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# An array slice literal. Unlike JavaScript's Array#slice, the second parameter
|
||||
# specifies the index of the end of the slice (just like the first parameter)
|
||||
# is the index of the beginning.
|
||||
class SliceNode < Node
|
||||
attr_reader :from, :to
|
||||
attr_reader :range
|
||||
|
||||
def initialize(from, to)
|
||||
@from, @to = from, to
|
||||
def initialize(range)
|
||||
@range = range
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
write(".slice(#{@from.compile(o)}, #{@to.compile(o)} + 1)")
|
||||
o = super(o)
|
||||
from = @range.from.compile(o)
|
||||
to = @range.to.compile(o)
|
||||
plus_part = @range.exclusive? ? '' : ' + 1'
|
||||
write(".slice(#{from}, #{to}#{plus_part})")
|
||||
end
|
||||
end
|
||||
|
||||
# Setting the value of a local variable, or the value of an object property.
|
||||
class AssignNode < Node
|
||||
LEADING_VAR = /\Avar\s+/
|
||||
PROTO_ASSIGN = /\A(\S+)\.prototype/
|
||||
|
||||
statement
|
||||
custom_return
|
||||
|
||||
attr_reader :variable, :value, :context
|
||||
@@ -332,19 +374,15 @@ module CoffeeScript
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable.to_s
|
||||
last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s
|
||||
name = @variable.compile(o)
|
||||
last = @variable.last.to_s
|
||||
proto = name[PROTO_ASSIGN, 1]
|
||||
o = o.merge(:assign => name, :last_assign => last, :proto_assign => proto)
|
||||
postfix = o[:return] ? ";\n#{o[:indent]}return #{name}" : ''
|
||||
return write("#{@variable}: #{@value.compile(o)}") if @context == :object
|
||||
return write("#{name} = #{@value.compile(o)}#{postfix}") if @variable.properties? && !@value.custom_assign?
|
||||
defined = o[:scope].find(name)
|
||||
def_part = defined || @variable.properties? ? "" : "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 = o.merge(:assign => @variable, :last_assign => last, :proto_assign => proto)
|
||||
return write("#{name}: #{@value.compile(o)}") if @context == :object
|
||||
o[:scope].find(name) unless @variable.properties?
|
||||
return write(@value.compile(o)) if @value.custom_assign?
|
||||
val = "#{name} = #{@value.compile(o)}"
|
||||
write(o[:return] && !@value.custom_return? ? "return (#{val})" : val)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -357,10 +395,10 @@ module CoffeeScript
|
||||
'and' => '&&',
|
||||
'or' => '||',
|
||||
'is' => '===',
|
||||
"aint" => "!==",
|
||||
'not' => '!',
|
||||
"isnt" => "!==",
|
||||
'not' => '!'
|
||||
}
|
||||
CONDITIONALS = ['||:', '&&:']
|
||||
CONDITIONALS = ['||=', '&&=']
|
||||
PREFIX_OPERATORS = ['typeof', 'delete']
|
||||
|
||||
attr_reader :operator, :first, :second
|
||||
@@ -411,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
|
||||
@@ -473,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
|
||||
@@ -500,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)
|
||||
@@ -536,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
|
||||
|
||||
@@ -585,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
|
||||
|
||||
@@ -594,7 +642,7 @@ module CoffeeScript
|
||||
end
|
||||
|
||||
def statement?
|
||||
@expressions.statement?
|
||||
@expressions.unwrap.statement?
|
||||
end
|
||||
|
||||
def custom_assign?
|
||||
@@ -606,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
|
||||
|
||||
@@ -679,8 +728,11 @@ module CoffeeScript
|
||||
# force sub-else bodies into statement form.
|
||||
def compile_statement(o)
|
||||
indent = o[:indent]
|
||||
cond_o = o.dup
|
||||
cond_o.delete(:assign)
|
||||
cond_o.delete(:return)
|
||||
o[:indent] += TAB
|
||||
if_part = "if (#{@condition.compile(o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
|
||||
if_part = "if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
|
||||
return if_part unless @else_body
|
||||
else_part = chain? ?
|
||||
" else #{@else_body.compile(o.merge(:indent => indent))}" :
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "coffee-script",
|
||||
"lib": "lib-js",
|
||||
"preload": ["coffee-script/loader"],
|
||||
"lib": "lib/coffee_script/narwhal/js",
|
||||
"preload": ["loader"],
|
||||
"description": "Unfancy JavaScript",
|
||||
"keywords": ["javascript", "language"],
|
||||
"author": "Jeremy Ashkenas",
|
||||
"version": "0.1.2"
|
||||
"version": "0.1.6"
|
||||
}
|
||||
|
||||
31
test/fixtures/each.js
vendored
31
test/fixtures/each.js
vendored
@@ -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;
|
||||
};
|
||||
})();
|
||||
1
test/fixtures/each.tokens
vendored
1
test/fixtures/each.tokens
vendored
@@ -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"], [".", "."]]
|
||||
29
test/fixtures/each_no_wrap.js
vendored
29
test/fixtures/each_no_wrap.js
vendored
@@ -1,29 +0,0 @@
|
||||
|
||||
// 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;
|
||||
};
|
||||
21
test/fixtures/execution/array_comprehension.js
vendored
21
test/fixtures/execution/array_comprehension.js
vendored
@@ -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');
|
||||
})();
|
||||
@@ -1,9 +0,0 @@
|
||||
(function(){
|
||||
var result;
|
||||
try {
|
||||
result = nonexistent * missing;
|
||||
} catch (error) {
|
||||
result = true;
|
||||
}
|
||||
print(result);
|
||||
})();
|
||||
27
test/fixtures/execution/calling_super.js
vendored
27
test/fixtures/execution/calling_super.js
vendored
@@ -1,27 +0,0 @@
|
||||
(function(){
|
||||
var Base = function() {
|
||||
};
|
||||
Base.prototype.func = function(string) {
|
||||
return 'zero/' + string;
|
||||
};
|
||||
var FirstChild = function() {
|
||||
};
|
||||
FirstChild.prototype.__proto__ = new Base();
|
||||
FirstChild.prototype.func = function(string) {
|
||||
return FirstChild.prototype.__proto__.func.call(this, 'one/') + string;
|
||||
};
|
||||
var SecondChild = function() {
|
||||
};
|
||||
SecondChild.prototype.__proto__ = new FirstChild();
|
||||
SecondChild.prototype.func = function(string) {
|
||||
return SecondChild.prototype.__proto__.func.call(this, 'two/') + string;
|
||||
};
|
||||
var ThirdChild = function() {
|
||||
};
|
||||
ThirdChild.prototype.__proto__ = new SecondChild();
|
||||
ThirdChild.prototype.func = function(string) {
|
||||
return ThirdChild.prototype.__proto__.func.call(this, 'three/') + string;
|
||||
};
|
||||
var result = (new ThirdChild()).func('four');
|
||||
print(result === 'zero/one/two/three/four');
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
10
test/fixtures/execution/keyword_operators.cs
vendored
10
test/fixtures/execution/keyword_operators.cs
vendored
@@ -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)
|
||||
10
test/fixtures/execution/keyword_operators.js
vendored
10
test/fixtures/execution/keyword_operators.js
vendored
@@ -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);
|
||||
})();
|
||||
@@ -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')
|
||||
@@ -3,17 +3,17 @@ Base.prototype.func: string =>
|
||||
'zero/' + string.
|
||||
|
||||
FirstChild: => .
|
||||
FirstChild extends new Base()
|
||||
FirstChild extends Base
|
||||
FirstChild.prototype.func: string =>
|
||||
super('one/') + string.
|
||||
|
||||
SecondChild: => .
|
||||
SecondChild extends new FirstChild()
|
||||
SecondChild extends FirstChild
|
||||
SecondChild.prototype.func: string =>
|
||||
super('two/') + string.
|
||||
|
||||
ThirdChild: => .
|
||||
ThirdChild extends new SecondChild()
|
||||
ThirdChild extends SecondChild
|
||||
ThirdChild.prototype.func: string =>
|
||||
super('three/') + string.
|
||||
|
||||
5
test/fixtures/execution/test_chained_calls.coffee
vendored
Normal file
5
test/fixtures/execution/test_chained_calls.coffee
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
identity_wrap: x => => x..
|
||||
|
||||
result: identity_wrap(identity_wrap(true))()()
|
||||
|
||||
print(result)
|
||||
@@ -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() == '-')
|
||||
29
test/fixtures/execution/test_everything.js
vendored
29
test/fixtures/execution/test_everything.js
vendored
@@ -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() === '-');
|
||||
})();
|
||||
3
test/fixtures/execution/test_lexical_scope.coffee
vendored
Normal file
3
test/fixtures/execution/test_lexical_scope.coffee
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
num: 1 + 2 + (a: 3)
|
||||
|
||||
print(num is 6)
|
||||
6
test/fixtures/execution/test_newline_escaping.coffee
vendored
Normal file
6
test/fixtures/execution/test_newline_escaping.coffee
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
six: \
|
||||
1 + \
|
||||
2 + \
|
||||
3
|
||||
|
||||
print(six is 6)
|
||||
8
test/fixtures/execution/test_range_comprehension.coffee
vendored
Normal file
8
test/fixtures/execution/test_range_comprehension.coffee
vendored
Normal 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')
|
||||
8
test/fixtures/execution/test_ranges_and_slices.coffee
vendored
Normal file
8
test/fixtures/execution/test_ranges_and_slices.coffee
vendored
Normal 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")
|
||||
16
test/fixtures/execution/test_switch.js
vendored
16
test/fixtures/execution/test_switch.js
vendored
@@ -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);
|
||||
})();
|
||||
@@ -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
36
test/fixtures/generation/each.js
vendored
Normal 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
1
test/fixtures/generation/each.tokens
vendored
Normal 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"], [".", "."]]
|
||||
34
test/fixtures/generation/each_no_wrap.js
vendored
Normal file
34
test/fixtures/generation/each_no_wrap.js
vendored
Normal 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;
|
||||
};
|
||||
@@ -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.
|
||||
@@ -3,28 +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|
|
||||
suceeded = `./shell #{source}`.chomp.to_sym == :true
|
||||
puts "failed: #{source}" unless suceeded
|
||||
assert suceeded
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,8 +24,8 @@ class ParserTest < Test::Unit::TestCase
|
||||
nodes = @par.parse("{one : 1 \n two : 2}").expressions
|
||||
obj = nodes.first.literal
|
||||
assert obj.is_a? ObjectNode
|
||||
assert obj.properties.first.variable == "one"
|
||||
assert obj.properties.last.variable == "two"
|
||||
assert obj.properties.first.variable.literal.value == "one"
|
||||
assert obj.properties.last.variable.literal.value == "two"
|
||||
end
|
||||
|
||||
def test_parsing_an_function_definition
|
||||
@@ -49,7 +49,7 @@ class ParserTest < Test::Unit::TestCase
|
||||
assert nodes.first.is_a? ForNode
|
||||
assert nodes.first.body.literal == 'i'
|
||||
assert nodes.first.filter.operator == '==='
|
||||
assert nodes.first.source.literal.objects.last.value == "5"
|
||||
assert nodes.first.source.literal.objects.last.literal.value == "5"
|
||||
end
|
||||
|
||||
def test_parsing_comment
|
||||
@@ -58,23 +58,29 @@ class ParserTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_parsing_inner_comments
|
||||
nodes = @par.parse(File.read('test/fixtures/inner_comments.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/each.cs'))
|
||||
assert nodes.compile(:no_wrap => true) == File.read('test/fixtures/each_no_wrap.js')
|
||||
nodes = @par.parse(File.read('test/fixtures/generation/each.coffee'))
|
||||
assert nodes.compile(:no_wrap => true) == File.read('test/fixtures/generation/each_no_wrap.js')
|
||||
end
|
||||
|
||||
def test_no_wrapping_parens_around_statements
|
||||
assert_raises(SyntaxError) do
|
||||
@par.parse("(try thing() catch error fail().)").compile
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user