mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-13 16:57:54 -05:00
Compare commits
326 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a61bbf005 | ||
|
|
c8d505e85d | ||
|
|
477c510345 | ||
|
|
c3029faca7 | ||
|
|
186797a745 | ||
|
|
d54fa2f2a1 | ||
|
|
5e1e949bf6 | ||
|
|
6c980d8adc | ||
|
|
2f63439bff | ||
|
|
c7cb308b6d | ||
|
|
9cc7d6af27 | ||
|
|
cfa357cbc3 | ||
|
|
9d8668f37f | ||
|
|
d1ddeacbe3 | ||
|
|
d9d09a9a72 | ||
|
|
a1528f3f19 | ||
|
|
7d2a955e0a | ||
|
|
24408c785a | ||
|
|
bb5bf7f94f | ||
|
|
3e987de4a8 | ||
|
|
de74bce2cd | ||
|
|
c265b7d5d6 | ||
|
|
ad18378f7e | ||
|
|
902febb43a | ||
|
|
13fc8aea04 | ||
|
|
8ea75290b5 | ||
|
|
9498ef9fe4 | ||
|
|
8e3922b6c6 | ||
|
|
2319affa61 | ||
|
|
7befbddae2 | ||
|
|
ae58e2ec6c | ||
|
|
4c3f00cf77 | ||
|
|
8b3926fb0c | ||
|
|
d416c184db | ||
|
|
30dca132bd | ||
|
|
69908be85c | ||
|
|
c70b6f39b1 | ||
|
|
fab98a2219 | ||
|
|
18c3e79a50 | ||
|
|
9ed8020b84 | ||
|
|
cf7079a379 | ||
|
|
3e33b4a618 | ||
|
|
a5c1922200 | ||
|
|
c49f2d8097 | ||
|
|
eb9b18376e | ||
|
|
fbcdc12a9c | ||
|
|
41bb6edcd2 | ||
|
|
22d3238b2a | ||
|
|
3124869e1d | ||
|
|
de49465dc6 | ||
|
|
4fc40e4841 | ||
|
|
0275e7775f | ||
|
|
001e42b85c | ||
|
|
46f1977ea1 | ||
|
|
c2bb93b5f8 | ||
|
|
1040e52246 | ||
|
|
d555685030 | ||
|
|
5fe419b1ce | ||
|
|
1ebc4d5f21 | ||
|
|
6658250c8e | ||
|
|
724d4c9660 | ||
|
|
0cf7801f36 | ||
|
|
ae603749be | ||
|
|
6e63a18f86 | ||
|
|
3e24cef69f | ||
|
|
2c1033f5da | ||
|
|
da9e38808c | ||
|
|
5efaff506c | ||
|
|
94bc7c1f92 | ||
|
|
f75d98e447 | ||
|
|
69283fcadd | ||
|
|
672dd70bdb | ||
|
|
32cd15f038 | ||
|
|
ecdb47107b | ||
|
|
cb1815885c | ||
|
|
d53d85d03e | ||
|
|
8e6486a1d7 | ||
|
|
97f16c0e9c | ||
|
|
bd2597c6b8 | ||
|
|
56015bd23f | ||
|
|
3724778989 | ||
|
|
bb0dfa5cd4 | ||
|
|
6160fe3c17 | ||
|
|
099944e0aa | ||
|
|
4814d5baa5 | ||
|
|
78c4957ba8 | ||
|
|
de4eddcad4 | ||
|
|
ba3c5298f7 | ||
|
|
21a0cc83ae | ||
|
|
7eff8786bc | ||
|
|
7ee5be674d | ||
|
|
38520bfece | ||
|
|
c3d0e50e8f | ||
|
|
34add7d7bf | ||
|
|
75d9e23df4 | ||
|
|
ff80f8d423 | ||
|
|
3489eec6ee | ||
|
|
2f69bc1708 | ||
|
|
0f81dbe913 | ||
|
|
e30a267c9d | ||
|
|
305f883eab | ||
|
|
f622fc43bc | ||
|
|
a93a570603 | ||
|
|
02fbd28e58 | ||
|
|
2cef93b381 | ||
|
|
caf9d627cb | ||
|
|
ad5b5fa458 | ||
|
|
41056ca2bd | ||
|
|
409283a30f | ||
|
|
abfc9f5a2d | ||
|
|
43edd29d22 | ||
|
|
26c89cef06 | ||
|
|
1d2bb3b2be | ||
|
|
f299972713 | ||
|
|
bfd7455db4 | ||
|
|
7ec91a1ee5 | ||
|
|
44fec922a4 | ||
|
|
07d0044718 | ||
|
|
00538bd62c | ||
|
|
4e8a12d70d | ||
|
|
edf1fc769f | ||
|
|
2231d67cef | ||
|
|
a376e7f4a3 | ||
|
|
20ade4f448 | ||
|
|
f9d630119d | ||
|
|
7f76c22894 | ||
|
|
b1fa06ff70 | ||
|
|
e481a39a0d | ||
|
|
8762162dff | ||
|
|
a6539a030c | ||
|
|
942572d081 | ||
|
|
ec60205014 | ||
|
|
dba8439425 | ||
|
|
774e3d3e31 | ||
|
|
cae0b1a038 | ||
|
|
bbbfd92373 | ||
|
|
fd83759ef4 | ||
|
|
49714656e1 | ||
|
|
d43d491561 | ||
|
|
c8711b419e | ||
|
|
5c7b77aa4d | ||
|
|
dc821f2e4c | ||
|
|
cd68f02981 | ||
|
|
e6cd32f2fc | ||
|
|
ed1c1f7a0e | ||
|
|
5659d1bd49 | ||
|
|
9a7c075e06 | ||
|
|
62c51006a1 | ||
|
|
d528db76e6 | ||
|
|
9a946d2ac5 | ||
|
|
c822f96145 | ||
|
|
9e3ef42c63 | ||
|
|
f93e552cb3 | ||
|
|
ea58be2838 | ||
|
|
4d514f255b | ||
|
|
ce0e71f796 | ||
|
|
7c97f32dd3 | ||
|
|
6ddd808f74 | ||
|
|
90051e9096 | ||
|
|
dd3c9abe15 | ||
|
|
d3ab60df78 | ||
|
|
541291e2f3 | ||
|
|
fd357871f2 | ||
|
|
f5aafc6591 | ||
|
|
96803d0129 | ||
|
|
ae112d6087 | ||
|
|
a3129e05ae | ||
|
|
f44443b58a | ||
|
|
3fbb870d01 | ||
|
|
cea417de02 | ||
|
|
ddd52a1845 | ||
|
|
50bdd2668c | ||
|
|
bb84a982ce | ||
|
|
7b5b261743 | ||
|
|
c84bb0c875 | ||
|
|
a186e47e2c | ||
|
|
41f3d54f4a | ||
|
|
9e47f6bd58 | ||
|
|
3762f4c680 | ||
|
|
845c8cba3b | ||
|
|
a722b42eed | ||
|
|
78e08f316d | ||
|
|
55616cf3b8 | ||
|
|
aa45456d7d | ||
|
|
ac00a19514 | ||
|
|
7ae8687a3e | ||
|
|
55e736cb27 | ||
|
|
3ee4e98ccc | ||
|
|
d7dd18b476 | ||
|
|
097bede5c8 | ||
|
|
983dac84a2 | ||
|
|
eeef8d3612 | ||
|
|
b4250c0f02 | ||
|
|
cc0c92d20d | ||
|
|
694833dbd0 | ||
|
|
fde9852090 | ||
|
|
191875a85b | ||
|
|
556f8cb68a | ||
|
|
5bed5646be | ||
|
|
08dddb27a0 | ||
|
|
2a1fc4b1b7 | ||
|
|
3c67d54bbe | ||
|
|
6a30d48ced | ||
|
|
d59ef71642 | ||
|
|
ff78546465 | ||
|
|
834442148b | ||
|
|
adaae0ccae | ||
|
|
7c132d65dc | ||
|
|
b58102c514 | ||
|
|
52700ca922 | ||
|
|
dd5317ebe9 | ||
|
|
1ba7c77136 | ||
|
|
62485c2b8c | ||
|
|
d46bf8ee71 | ||
|
|
e494d520ea | ||
|
|
f55b4cd8b6 | ||
|
|
763e04fec9 | ||
|
|
4b5db1181c | ||
|
|
bc6ec37272 | ||
|
|
55bf0a2c61 | ||
|
|
968aeec523 | ||
|
|
851ec82495 | ||
|
|
c4ba971d39 | ||
|
|
e7e8e243a2 | ||
|
|
73aaf127c8 | ||
|
|
7a0de52c96 | ||
|
|
2d57ee693b | ||
|
|
d8ceb3b4bb | ||
|
|
beae912a91 | ||
|
|
6865f5be92 | ||
|
|
2f35bba083 | ||
|
|
378e156e11 | ||
|
|
9b2326492b | ||
|
|
93009e07f6 | ||
|
|
985e0a080b | ||
|
|
95bfb0b45c | ||
|
|
65e9ba4c30 | ||
|
|
1c83e68292 | ||
|
|
47812d9ea6 | ||
|
|
a80b532a05 | ||
|
|
42ca566d26 | ||
|
|
e27756cee8 | ||
|
|
46f81c2de1 | ||
|
|
c3ff752c40 | ||
|
|
73a0777705 | ||
|
|
9f80998415 | ||
|
|
dbccc1fa4f | ||
|
|
08248180f9 | ||
|
|
ed504ea6be | ||
|
|
da84906d1e | ||
|
|
aafd3cd6ea | ||
|
|
39ceca477d | ||
|
|
bfb24c6b5d | ||
|
|
7f502543d2 | ||
|
|
5d1ec9d2a9 | ||
|
|
2e8f03b4cc | ||
|
|
840e2b89fc | ||
|
|
c438123a3d | ||
|
|
726a9b229e | ||
|
|
8d76f4bd3f | ||
|
|
849f0e4192 | ||
|
|
47928ca952 | ||
|
|
fb13a303f2 | ||
|
|
b743e3219a | ||
|
|
417753bd62 | ||
|
|
705b293982 | ||
|
|
0a58eeef2b | ||
|
|
b1f3ad24a2 | ||
|
|
2b94849429 | ||
|
|
eae53d4787 | ||
|
|
5f9a190683 | ||
|
|
cdfb5091be | ||
|
|
64879cdc66 | ||
|
|
a4d014549b | ||
|
|
60eabf63cf | ||
|
|
6555473788 | ||
|
|
e916d4648d | ||
|
|
ec58d6fda2 | ||
|
|
65809d08f6 | ||
|
|
d45643c527 | ||
|
|
9b8f018646 | ||
|
|
2f211196a2 | ||
|
|
9bb3e3fbe8 | ||
|
|
c7fa9c320a | ||
|
|
dcc70e5ab0 | ||
|
|
93bdfcb72a | ||
|
|
9ba1ffde21 | ||
|
|
0b2e7f1e59 | ||
|
|
b2e6a34d40 | ||
|
|
ad3b887df4 | ||
|
|
6f81ac3684 | ||
|
|
2f75854a61 | ||
|
|
253e45fc54 | ||
|
|
31639d7d1a | ||
|
|
6ba5d45cbe | ||
|
|
f154ab3d15 | ||
|
|
669c065dd7 | ||
|
|
92adabdddc | ||
|
|
049358d006 | ||
|
|
a50446a0ca | ||
|
|
67bb49ed04 | ||
|
|
1395b05d36 | ||
|
|
83944950ac | ||
|
|
d124f7fc0d | ||
|
|
77704d24a2 | ||
|
|
68bc68c1ac | ||
|
|
8511a33b1e | ||
|
|
6050cad0f8 | ||
|
|
9976de76f5 | ||
|
|
0dc445138b | ||
|
|
3ffbf541df | ||
|
|
e3c667d49d | ||
|
|
2c90e8b002 | ||
|
|
398251ff90 | ||
|
|
9249ceaef5 | ||
|
|
1eec05d23a | ||
|
|
1590713576 | ||
|
|
146b5694c2 | ||
|
|
1d35910567 | ||
|
|
1a8311b9d0 | ||
|
|
4e1e119f58 | ||
|
|
3dac0f6d84 | ||
|
|
962885444e | ||
|
|
d1682f5b3f | ||
|
|
c6f11fbfeb | ||
|
|
290aa257de |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1 +1,6 @@
|
||||
parser.output
|
||||
presentation
|
||||
test.coffee
|
||||
parser.output
|
||||
lib/coffee_script/parser.rb
|
||||
test/fixtures/underscore
|
||||
*.gem
|
||||
22
LICENSE
Normal file
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2010 Jeremy Ashkenas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
41
README
Normal file
41
README
Normal file
@@ -0,0 +1,41 @@
|
||||
=
|
||||
{
|
||||
} } {
|
||||
{ { } }
|
||||
} }{ {
|
||||
{ }{ } } _____ __ __
|
||||
( }{ }{ { ) / ____| / _|/ _|
|
||||
.- { { } { }} -. | | ___ | |_| |_ ___ ___
|
||||
( ( } { } { } } ) | | / _ \| _| _/ _ \/ _ \
|
||||
|`-..________ ..-'| | |___| (_) | | | || __/ __/
|
||||
| | \_____\___/|_| |_| \___|\___|
|
||||
| ;--.
|
||||
| (__ \ _____ _ _
|
||||
| | ) ) / ____| (_) | |
|
||||
| |/ / | (___ ___ _ __ _ _ __ | |_
|
||||
| ( / \___ \ / __| '__| | '_ \| __|
|
||||
| |/ ____) | (__| | | | |_) | |_
|
||||
| | |_____/ \___|_| |_| .__/ \__|
|
||||
`-.._________..-' | |
|
||||
|_|
|
||||
|
||||
|
||||
CoffeeScript is a little language that compiles into JavaScript.
|
||||
|
||||
Install the compiler:
|
||||
gem install coffee-script
|
||||
|
||||
Compile a script:
|
||||
coffee /path/to/script.coffee
|
||||
|
||||
For documentation, usage, and examples, see:
|
||||
http://jashkenas.github.com/coffee-script/
|
||||
|
||||
To suggest a feature or report a bug:
|
||||
http://github.com/jashkenas/coffee-script/issues/
|
||||
|
||||
The source repository:
|
||||
git://github.com/jashkenas/coffee-script.git
|
||||
|
||||
To build CoffeeScript from source, install the "racc" gem and
|
||||
run "rake build:parser". Then bin/coffee will work.
|
||||
72
Rakefile
Normal file
72
Rakefile
Normal file
@@ -0,0 +1,72 @@
|
||||
require 'erb'
|
||||
require 'fileutils'
|
||||
require 'rake/testtask'
|
||||
|
||||
desc "Run all tests"
|
||||
task :test do
|
||||
$LOAD_PATH.unshift(File.expand_path('test'))
|
||||
require 'redgreen' if Gem.available?('redgreen')
|
||||
require 'test/unit'
|
||||
Dir['test/*/**/test_*.rb'].each {|test| require test }
|
||||
end
|
||||
|
||||
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/lib/coffee-script"
|
||||
sh "mv lib/coffee_script/narwhal/lib/coffee-script/coffee-script.js lib/coffee_script/narwhal/lib/coffee-script.js"
|
||||
end
|
||||
|
||||
desc "Compile and install the Ultraviolet syntax highlighter"
|
||||
task :ultraviolet do
|
||||
sh "plist2syntax lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage"
|
||||
sh "sudo mv coffeescript.yaml /usr/local/lib/ruby/gems/1.8/gems/ultraviolet-0.10.2/syntax/coffeescript.syntax"
|
||||
end
|
||||
|
||||
desc "Rebuild the Underscore.coffee documentation page"
|
||||
task :underscore do
|
||||
sh "uv -s coffeescript -t idle -h examples/underscore.coffee > documentation/underscore.html"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
desc "Build the documentation page"
|
||||
task :doc do
|
||||
source = 'documentation/index.html.erb'
|
||||
child = fork { exec "bin/coffee documentation/coffee/*.coffee -o documentation/js -w" }
|
||||
at_exit { Process.kill("INT", child) }
|
||||
Signal.trap("INT") { exit }
|
||||
# `uv -t idle -s coffeescript -h examples/underscore.coffee > documentation/underscore.html`
|
||||
loop do
|
||||
mtime = File.stat(source).mtime
|
||||
if !@mtime || mtime > @mtime
|
||||
rendered = ERB.new(File.read(source)).result(binding)
|
||||
File.open('index.html', 'w+') {|f| f.write(rendered) }
|
||||
end
|
||||
@mtime = mtime
|
||||
sleep 1
|
||||
end
|
||||
end
|
||||
|
||||
namespace :gem do
|
||||
|
||||
desc 'Build and install the coffee-script gem'
|
||||
task :install do
|
||||
sh "gem build coffee-script.gemspec"
|
||||
sh "sudo gem install #{Dir['*.gem'].join(' ')} --local --no-ri --no-rdoc"
|
||||
end
|
||||
|
||||
desc 'Uninstall the coffee-script gem'
|
||||
task :uninstall do
|
||||
sh "sudo gem uninstall -x coffee-script"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
task :default => :test
|
||||
30
SYNTAX
30
SYNTAX
@@ -1,30 +0,0 @@
|
||||
Every line is an expression. Multiple expressions on a single line can be
|
||||
separated by a ";" character.
|
||||
|
||||
NUM: 1
|
||||
1.0
|
||||
|
||||
STRING: "hello"
|
||||
'hello'
|
||||
|
||||
OBJECT: {one : 1, two : 2}
|
||||
|
||||
ARRAY: [1, 2, 3]
|
||||
|
||||
CODE: a, b => a * b.
|
||||
|
||||
IF: return x if x > 1
|
||||
|
||||
if (x > 1) return x
|
||||
|
||||
ASSIGN: a : b
|
||||
|
||||
LOGICAL: x && y
|
||||
x and y
|
||||
x || y
|
||||
x or y
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
6
WISHLIST
6
WISHLIST
@@ -1,6 +0,0 @@
|
||||
* Is it possible to close blocks (functions, ifs, trys) without an explicit
|
||||
block delimiter or significant whitespace?
|
||||
|
||||
* Is it possible to pass comments through cleanly and have them show up on
|
||||
the other end? This includes comments in the middle of array and object
|
||||
literals, and argument lists.
|
||||
5
bin/coffee
Executable file
5
bin/coffee
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "#{File.dirname(__FILE__)}/../lib/coffee_script/command_line.rb"
|
||||
|
||||
CoffeeScript::CommandLine.new
|
||||
126
code.cs
126
code.cs
@@ -1,126 +0,0 @@
|
||||
# TODO: Add range indexing: array[5..7] => array.slice(5, 7)
|
||||
|
||||
# Functions:
|
||||
square: x => x * x.
|
||||
|
||||
sum: x, y => x + y.
|
||||
|
||||
odd: x => x % 2 is 0.
|
||||
|
||||
even: x => x % 2 aint 0.
|
||||
|
||||
run_loop: =>
|
||||
fire_events( e => e.stopPropagation(). )
|
||||
listen()
|
||||
wait().
|
||||
|
||||
# Objects:
|
||||
dense_object_literal: {one: 1, two: 2, three: 3}
|
||||
|
||||
spaced_out_multiline_object: {
|
||||
|
||||
pi: 3.14159
|
||||
|
||||
list: [1, 2, 3, 4]
|
||||
|
||||
three: new Idea()
|
||||
|
||||
inner_obj: {
|
||||
freedom: => _.freedom().
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Arrays:
|
||||
stooges : [{moe: 45}, {curly: 43}, {larry: 46}]
|
||||
|
||||
exponents : [x => x., x => x * x., x => x * x * x.]
|
||||
|
||||
empty: []
|
||||
|
||||
# Conditionals and ternaries.
|
||||
if submarine.shields_up
|
||||
full_speed_ahead()
|
||||
fire_torpedos()
|
||||
else
|
||||
run_away().
|
||||
|
||||
eldest: if 25 > 21 then liz else marge.
|
||||
|
||||
decoration: medal_of_honor if war_hero
|
||||
|
||||
go_to_sleep() unless coffee
|
||||
|
||||
# Returning early:
|
||||
race: =>
|
||||
run()
|
||||
walk()
|
||||
crawl()
|
||||
if tired then return sleep().
|
||||
race().
|
||||
|
||||
# Conditional operators:
|
||||
good ||= evil
|
||||
wine &&= cheese
|
||||
|
||||
# Nested property access and calls.
|
||||
((moon.turn(360))).shapes[3].move({x: 45, y: 30}).position
|
||||
|
||||
a: b: c: 5
|
||||
|
||||
# Embedded JavaScript.
|
||||
callback(
|
||||
`function(e) { e.stop(); }`
|
||||
)
|
||||
|
||||
# Try/Catch/Finally/Throw.
|
||||
try
|
||||
all_hell_breaks_loose()
|
||||
dogs_and_cats_living_together()
|
||||
throw "up"
|
||||
catch error
|
||||
print( error )
|
||||
finally
|
||||
clean_up().
|
||||
|
||||
try all_hell_breaks_loose() catch error print(error) finally clean_up().
|
||||
|
||||
# While loops.
|
||||
while demand > supply
|
||||
sell()
|
||||
restock().
|
||||
|
||||
while supply > demand then buy().
|
||||
|
||||
# Unary operators.
|
||||
!!true
|
||||
|
||||
# Lexical scoping.
|
||||
a: 5
|
||||
change_a_and_set_b: =>
|
||||
a: 10
|
||||
b: 15.
|
||||
b: 20
|
||||
|
||||
# Array comprehensions.
|
||||
supper: food.capitalize() for food in ['toast', 'cheese', 'wine'].
|
||||
|
||||
drink(bottle) for bottle, i in ['soda', 'wine', 'lemonade'] if even(i).
|
||||
|
||||
# Switch statements.
|
||||
switch day
|
||||
case "Tuesday" then eat_breakfast()
|
||||
case "Sunday" then go_to_church()
|
||||
case "Saturday" then go_to_the_park()
|
||||
case "Wednesday"
|
||||
eat_breakfast()
|
||||
go_to_work()
|
||||
eat_dinner()
|
||||
default go_to_work().
|
||||
|
||||
# Semicolons can optionally be used instead of newlines.
|
||||
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]
|
||||
26
coffee-script.gemspec
Normal file
26
coffee-script.gemspec
Normal file
@@ -0,0 +1,26 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'coffee-script'
|
||||
s.version = '0.2.4' # Keep version in sync with coffee-script.rb
|
||||
s.date = '2010-1-12'
|
||||
|
||||
s.homepage = "http://jashkenas.github.com/coffee-script/"
|
||||
s.summary = "The CoffeeScript Compiler"
|
||||
s.description = <<-EOS
|
||||
CoffeeScript is a little language that compiles into JavaScript. Think
|
||||
of it as JavaScript's less ostentatious kid brother -- the same genes,
|
||||
roughly the same height, but a different sense of style. Apart from a
|
||||
handful of bonus goodies, statements in CoffeeScript correspond
|
||||
one-to-one with their equivalent in JavaScript, it's just another
|
||||
way of saying it.
|
||||
EOS
|
||||
|
||||
s.authors = ['Jeremy Ashkenas']
|
||||
s.email = 'jashkenas@gmail.com'
|
||||
s.rubyforge_project = 'coffee-script'
|
||||
s.has_rdoc = false
|
||||
|
||||
s.require_paths = ['lib']
|
||||
s.executables = ['coffee']
|
||||
|
||||
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README', 'package.json']
|
||||
end
|
||||
7
documentation/coffee/aliases.coffee
Normal file
7
documentation/coffee/aliases.coffee
Normal file
@@ -0,0 +1,7 @@
|
||||
launch() if ignition is on
|
||||
|
||||
volume: 10 if band isnt spinal_tap
|
||||
|
||||
let_the_wild_rumpus_begin() unless answer is no
|
||||
|
||||
if car.speed < speed_limit then accelerate()
|
||||
4
documentation/coffee/arguments.coffee
Normal file
4
documentation/coffee/arguments.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
backwards: =>
|
||||
alert(arguments.reverse())
|
||||
|
||||
backwards("stairway", "to", "heaven")
|
||||
7
documentation/coffee/array_comprehensions.coffee
Normal file
7
documentation/coffee/array_comprehensions.coffee
Normal file
@@ -0,0 +1,7 @@
|
||||
# Eat lunch.
|
||||
lunch: eat(food) for food in ['toast', 'cheese', 'wine']
|
||||
|
||||
# Naive collision detection.
|
||||
for roid in asteroids
|
||||
for roid2 in asteroids when roid isnt roid2
|
||||
roid.explode() if roid.overlaps(roid2)
|
||||
2
documentation/coffee/assignment.coffee
Normal file
2
documentation/coffee/assignment.coffee
Normal file
@@ -0,0 +1,2 @@
|
||||
greeting: "Hello CoffeeScript"
|
||||
difficulty: 0.5
|
||||
4
documentation/coffee/blocks.coffee
Normal file
4
documentation/coffee/blocks.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
$('table.list').each() table =>
|
||||
$('tr.account', table).each() row =>
|
||||
row.show()
|
||||
row.highlight()
|
||||
9
documentation/coffee/conditionals.coffee
Normal file
9
documentation/coffee/conditionals.coffee
Normal file
@@ -0,0 +1,9 @@
|
||||
mood: greatly_improved if singing
|
||||
|
||||
if happy and knows_it
|
||||
claps_hands()
|
||||
cha_cha_cha()
|
||||
|
||||
date: if friday then sue else jill
|
||||
|
||||
expensive ||= do_the_math()
|
||||
5
documentation/coffee/embedded.coffee
Normal file
5
documentation/coffee/embedded.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
hi: `function() {
|
||||
return [document.title, "Hello JavaScript"].join(": ");
|
||||
}`
|
||||
|
||||
|
||||
1
documentation/coffee/existence.coffee
Normal file
1
documentation/coffee/existence.coffee
Normal file
@@ -0,0 +1 @@
|
||||
solipsism: true if mind? and not world?
|
||||
9
documentation/coffee/expressions.coffee
Normal file
9
documentation/coffee/expressions.coffee
Normal file
@@ -0,0 +1,9 @@
|
||||
grade: student =>
|
||||
if student.excellent_work
|
||||
"A+"
|
||||
else if student.okay_stuff
|
||||
if student.tried_hard then "B" else "B-"
|
||||
else
|
||||
"C"
|
||||
|
||||
eldest: if 24 > 21 then "Liz" else "Ike"
|
||||
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)
|
||||
3
documentation/coffee/expressions_comprehension.coffee
Normal file
3
documentation/coffee/expressions_comprehension.coffee
Normal file
@@ -0,0 +1,3 @@
|
||||
# The first ten global properties.
|
||||
|
||||
globals: (name for name of window)[0...10]
|
||||
6
documentation/coffee/expressions_try.coffee
Normal file
6
documentation/coffee/expressions_try.coffee
Normal file
@@ -0,0 +1,6 @@
|
||||
alert(
|
||||
try
|
||||
nonexistent / undefined
|
||||
catch error
|
||||
"Caught an error: " + error
|
||||
)
|
||||
2
documentation/coffee/functions.coffee
Normal file
2
documentation/coffee/functions.coffee
Normal file
@@ -0,0 +1,2 @@
|
||||
square: x => x * x
|
||||
cube: x => square(x) * x
|
||||
5
documentation/coffee/heredocs.coffee
Normal file
5
documentation/coffee/heredocs.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
html: '''
|
||||
<strong>
|
||||
cup of coffeescript
|
||||
</strong>
|
||||
'''
|
||||
5
documentation/coffee/multiple_return_values.coffee
Normal file
5
documentation/coffee/multiple_return_values.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
weather_report: location =>
|
||||
# Make an Ajax request to fetch the weather...
|
||||
[location, 72, "Mostly Sunny"]
|
||||
|
||||
[city, temp, forecast]: weather_report("Berkeley, CA")
|
||||
4
documentation/coffee/object_comprehensions.coffee
Normal file
4
documentation/coffee/object_comprehensions.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
years_old: {max: 10, ida: 9, tim: 11}
|
||||
|
||||
ages: for child, age of years_old
|
||||
child + " is " + age
|
||||
13
documentation/coffee/object_extraction.coffee
Normal file
13
documentation/coffee/object_extraction.coffee
Normal file
@@ -0,0 +1,13 @@
|
||||
futurists: {
|
||||
sculptor: "Umberto Boccioni"
|
||||
painter: "Vladimir Burliuk"
|
||||
poet: {
|
||||
name: "F.T. Marinetti"
|
||||
address: [
|
||||
"Via Roma 42R"
|
||||
"Bellagio, Italy 22021"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
{poet: {name: poet, address: [street, city]}}: futurists
|
||||
13
documentation/coffee/objects_and_arrays.coffee
Normal file
13
documentation/coffee/objects_and_arrays.coffee
Normal file
@@ -0,0 +1,13 @@
|
||||
song: ["do", "re", "mi", "fa", "so"]
|
||||
|
||||
ages: {
|
||||
max: 10
|
||||
ida: 9
|
||||
tim: 11
|
||||
}
|
||||
|
||||
matrix: [
|
||||
1, 0, 1
|
||||
0, 0, 1
|
||||
1, 1, 0
|
||||
]
|
||||
29
documentation/coffee/overview.coffee
Normal file
29
documentation/coffee/overview.coffee
Normal file
@@ -0,0 +1,29 @@
|
||||
# Assignment:
|
||||
number: 42
|
||||
opposite_day: true
|
||||
|
||||
# Conditions:
|
||||
number: -42 if opposite_day
|
||||
|
||||
# Functions:
|
||||
square: x => x * x
|
||||
|
||||
# Arrays:
|
||||
list: [1, 2, 3, 4, 5]
|
||||
|
||||
# Objects:
|
||||
math: {
|
||||
root: Math.sqrt
|
||||
square: square
|
||||
cube: x => x * square(x)
|
||||
}
|
||||
|
||||
# Splats:
|
||||
race: winner, runners... =>
|
||||
print(winner, runners)
|
||||
|
||||
# Existence:
|
||||
alert("I knew it!") if elvis?
|
||||
|
||||
# Array comprehensions:
|
||||
cubed_list: math.cube(num) for num in list
|
||||
4
documentation/coffee/parallel_assignment.coffee
Normal file
4
documentation/coffee/parallel_assignment.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
bait: 1000
|
||||
and_switch: 0
|
||||
|
||||
[bait, and_switch]: [and_switch, bait]
|
||||
6
documentation/coffee/range_comprehensions.coffee
Normal file
6
documentation/coffee/range_comprehensions.coffee
Normal file
@@ -0,0 +1,6 @@
|
||||
countdown: num for num in [10..1]
|
||||
|
||||
egg_delivery: =>
|
||||
for i in [0...eggs.length] by 12
|
||||
dozen_eggs: eggs[i...i+12]
|
||||
deliver(new egg_carton(dozen))
|
||||
5
documentation/coffee/scope.coffee
Normal file
5
documentation/coffee/scope.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
num: 1
|
||||
change_numbers: =>
|
||||
new_num: -1
|
||||
num: 10
|
||||
new_num: change_numbers()
|
||||
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]
|
||||
|
||||
25
documentation/coffee/splats.coffee
Normal file
25
documentation/coffee/splats.coffee
Normal file
@@ -0,0 +1,25 @@
|
||||
gold: silver: the_field: "unknown"
|
||||
|
||||
medalists: first, second, rest... =>
|
||||
gold: first
|
||||
silver: second
|
||||
the_field: rest
|
||||
|
||||
contenders: [
|
||||
"Michael Phelps"
|
||||
"Liu Xiang"
|
||||
"Yao Ming"
|
||||
"Allyson Felix"
|
||||
"Shawn Johnson"
|
||||
"Roman Sebrle"
|
||||
"Guo Jingjing"
|
||||
"Tyson Gay"
|
||||
"Asafa Powell"
|
||||
"Usain Bolt"
|
||||
]
|
||||
|
||||
medalists(contenders...)
|
||||
|
||||
alert("Gold: " + gold)
|
||||
alert("Silver: " + silver)
|
||||
alert("The Field: " + the_field)
|
||||
5
documentation/coffee/splices.coffee
Normal file
5
documentation/coffee/splices.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
|
||||
numbers[3..6]: [-3, -4, -5, -6]
|
||||
|
||||
|
||||
8
documentation/coffee/strings.coffee
Normal file
8
documentation/coffee/strings.coffee
Normal file
@@ -0,0 +1,8 @@
|
||||
moby_dick: "Call me Ishmael. Some years ago --
|
||||
never mind how long precisely -- having little
|
||||
or no money in my purse, and nothing particular
|
||||
to interest me on shore, I thought I would sail
|
||||
about a little and see the watery part of the
|
||||
world..."
|
||||
|
||||
|
||||
25
documentation/coffee/super.coffee
Normal file
25
documentation/coffee/super.coffee
Normal file
@@ -0,0 +1,25 @@
|
||||
Animal: =>
|
||||
Animal::move: meters =>
|
||||
alert(this.name + " moved " + meters + "m.")
|
||||
|
||||
Snake: name => this.name: name
|
||||
Snake extends Animal
|
||||
Snake::move: =>
|
||||
alert("Slithering...")
|
||||
super(5)
|
||||
|
||||
Horse: name => this.name: name
|
||||
Horse extends Animal
|
||||
Horse::move: =>
|
||||
alert("Galloping...")
|
||||
super(45)
|
||||
|
||||
sam: new Snake("Sammy the Python")
|
||||
tom: new Horse("Tommy the Palomino")
|
||||
|
||||
sam.move()
|
||||
tom.move()
|
||||
|
||||
|
||||
|
||||
|
||||
9
documentation/coffee/switch.coffee
Normal file
9
documentation/coffee/switch.coffee
Normal file
@@ -0,0 +1,9 @@
|
||||
switch day
|
||||
when "Tuesday" then eat_breakfast()
|
||||
when "Wednesday" then go_to_the_park()
|
||||
when "Saturday"
|
||||
if day is bingo_day
|
||||
go_to_bingo()
|
||||
go_dancing()
|
||||
when "Sunday" then go_to_church()
|
||||
else go_to_work()
|
||||
7
documentation/coffee/try.coffee
Normal file
7
documentation/coffee/try.coffee
Normal file
@@ -0,0 +1,7 @@
|
||||
try
|
||||
all_hell_breaks_loose()
|
||||
cats_and_dogs_living_together()
|
||||
catch error
|
||||
print(error)
|
||||
finally
|
||||
clean_up()
|
||||
5
documentation/coffee/while.coffee
Normal file
5
documentation/coffee/while.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
while demand > supply
|
||||
sell()
|
||||
restock()
|
||||
|
||||
while supply > demand then buy()
|
||||
147
documentation/css/amy.css
Normal file
147
documentation/css/amy.css
Normal file
@@ -0,0 +1,147 @@
|
||||
pre.amy .PolymorphicVariants {
|
||||
color: #60B0FF;
|
||||
font-style: italic;
|
||||
}
|
||||
pre.amy .KeywordDecorator {
|
||||
color: #D0D0FF;
|
||||
}
|
||||
pre.amy .Punctuation {
|
||||
color: #805080;
|
||||
}
|
||||
pre.amy .InheritedClass {
|
||||
}
|
||||
pre.amy .InvalidDepricated {
|
||||
background-color: #CC66FF;
|
||||
color: #200020;
|
||||
}
|
||||
pre.amy .LibraryVariable {
|
||||
}
|
||||
pre.amy .TokenReferenceOcamlyacc {
|
||||
color: #3CB0D0;
|
||||
}
|
||||
pre.amy .Storage {
|
||||
color: #B0FFF0;
|
||||
}
|
||||
pre.amy .KeywordOperator {
|
||||
color: #A0A0FF;
|
||||
}
|
||||
pre.amy .CharacterConstant {
|
||||
color: #666666;
|
||||
}
|
||||
pre.amy .line-numbers {
|
||||
background-color: #800000;
|
||||
color: #000000;
|
||||
}
|
||||
pre.amy .ClassName {
|
||||
color: #70E080;
|
||||
}
|
||||
pre.amy .Int64Constant {
|
||||
font-style: italic;
|
||||
}
|
||||
pre.amy .NonTerminalReferenceOcamlyacc {
|
||||
color: #C0F0F0;
|
||||
}
|
||||
pre.amy .TokenDefinitionOcamlyacc {
|
||||
color: #3080A0;
|
||||
}
|
||||
pre.amy .ClassType {
|
||||
color: #70E0A0;
|
||||
}
|
||||
pre.amy .ControlKeyword {
|
||||
color: #80A0FF;
|
||||
}
|
||||
pre.amy .LineNumberDirectives {
|
||||
text-decoration: underline;
|
||||
color: #C080C0;
|
||||
}
|
||||
pre.amy .FloatingPointConstant {
|
||||
text-decoration: underline;
|
||||
}
|
||||
pre.amy .Int32Constant {
|
||||
font-weight: bold;
|
||||
}
|
||||
pre.amy .TagName {
|
||||
color: #009090;
|
||||
}
|
||||
pre.amy .ModuleTypeDefinitions {
|
||||
text-decoration: underline;
|
||||
color: #B000B0;
|
||||
}
|
||||
pre.amy .Integer {
|
||||
color: #7090B0;
|
||||
}
|
||||
pre.amy .Camlp4TempParser {
|
||||
}
|
||||
pre.amy .InvalidIllegal {
|
||||
font-weight: bold;
|
||||
background-color: #FFFF00;
|
||||
color: #400080;
|
||||
}
|
||||
pre.amy .LibraryConstant {
|
||||
background-color: #200020;
|
||||
}
|
||||
pre.amy .ModuleDefinitions {
|
||||
color: #B000B0;
|
||||
}
|
||||
pre.amy .Variants {
|
||||
color: #60B0FF;
|
||||
}
|
||||
pre.amy .CompilerDirectives {
|
||||
color: #C080C0;
|
||||
}
|
||||
pre.amy .FloatingPointInfixOperator {
|
||||
text-decoration: underline;
|
||||
}
|
||||
pre.amy .BuiltInConstant1 {
|
||||
}
|
||||
pre.amy {
|
||||
background-color: #200020;
|
||||
color: #D0D0FF;
|
||||
}
|
||||
pre.amy .FunctionArgument {
|
||||
color: #80B0B0;
|
||||
}
|
||||
pre.amy .FloatingPointPrefixOperator {
|
||||
text-decoration: underline;
|
||||
}
|
||||
pre.amy .NativeintConstant {
|
||||
font-weight: bold;
|
||||
}
|
||||
pre.amy .BuiltInConstant {
|
||||
color: #707090;
|
||||
}
|
||||
pre.amy .BooleanConstant {
|
||||
color: #8080A0;
|
||||
}
|
||||
pre.amy .LibraryClassType {
|
||||
}
|
||||
pre.amy .TagAttribute {
|
||||
}
|
||||
pre.amy .Keyword {
|
||||
color: #A080FF;
|
||||
}
|
||||
pre.amy .UserDefinedConstant {
|
||||
}
|
||||
pre.amy .String {
|
||||
color: #999999;
|
||||
}
|
||||
pre.amy .Camlp4Code {
|
||||
background-color: #350060;
|
||||
}
|
||||
pre.amy .NonTerminalDefinitionOcamlyacc {
|
||||
color: #90E0E0;
|
||||
}
|
||||
pre.amy .FunctionName {
|
||||
color: #50A0A0;
|
||||
}
|
||||
pre.amy .SupportModules {
|
||||
color: #A00050;
|
||||
}
|
||||
pre.amy .Variable {
|
||||
color: #008080;
|
||||
}
|
||||
pre.amy .Comment {
|
||||
background-color: #200020;
|
||||
color: #404080;
|
||||
font-style: italic;
|
||||
}
|
||||
87
documentation/css/docs.css
Normal file
87
documentation/css/docs.css
Normal file
@@ -0,0 +1,87 @@
|
||||
body {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
background: #f3f3f9;
|
||||
color: #191933;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
div.container {
|
||||
width: 850px;
|
||||
margin: 50px 0 50px 50px;
|
||||
}
|
||||
p {
|
||||
padding-left: 13px;
|
||||
width: 625px;
|
||||
}
|
||||
a {
|
||||
color: #000055;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
padding-left: 13px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
br.clear {
|
||||
height: 0;
|
||||
clear: both;
|
||||
}
|
||||
b.header {
|
||||
color: #000055;
|
||||
display: block;
|
||||
margin: 40px 0 5px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
li {
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
table {
|
||||
margin: 16px 0 0 13px; padding: 0;
|
||||
width: 625px;
|
||||
}
|
||||
tr, td {
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
td {
|
||||
padding: 9px 15px 9px 0;
|
||||
}
|
||||
code, pre, tt {
|
||||
font-family: Monaco, Consolas, "Lucida Console", monospace;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #191955;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
tt {
|
||||
background: #f8f8ff;
|
||||
border: 1px solid #dedede;
|
||||
font-size: 85%;
|
||||
padding: 0px 0.2em;
|
||||
}
|
||||
pre {
|
||||
border-left: 6px solid #222255;
|
||||
margin-left: 13px;
|
||||
padding: 3px 0 3px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
div.code {
|
||||
position: relative;
|
||||
border: 1px solid #cacaca;
|
||||
background: #fff;
|
||||
padding: 7px 0 10px 0;
|
||||
-moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px;
|
||||
-webkit-box-shadow: 0px 0px 7px #cacaca;
|
||||
}
|
||||
div.code button {
|
||||
position: absolute;
|
||||
right: 8px; bottom: 8px;
|
||||
}
|
||||
div.code pre {
|
||||
float: left;
|
||||
width: 410px;
|
||||
border-left: 1px dotted #559;
|
||||
padding: 0 0 0 12px;
|
||||
margin: 0;
|
||||
}
|
||||
div.code pre:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
62
documentation/css/idle.css
Normal file
62
documentation/css/idle.css
Normal file
@@ -0,0 +1,62 @@
|
||||
pre.idle .InheritedClass {
|
||||
}
|
||||
pre.idle .TypeName {
|
||||
color: #21439C;
|
||||
}
|
||||
pre.idle .Number {
|
||||
}
|
||||
pre.idle .LibraryVariable {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .Storage {
|
||||
color: #FF5600;
|
||||
}
|
||||
pre.idle .line-numbers {
|
||||
background-color: #BAD6FD;
|
||||
color: #000000;
|
||||
}
|
||||
pre.idle {
|
||||
background-color: #FFFFFF;
|
||||
color: #000000;
|
||||
}
|
||||
pre.idle .StringInterpolation {
|
||||
color: #990000;
|
||||
}
|
||||
pre.idle .TagName {
|
||||
}
|
||||
pre.idle .LibraryConstant {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .FunctionArgument {
|
||||
}
|
||||
pre.idle .BuiltInConstant {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .Invalid {
|
||||
background-color: #990000;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
pre.idle .LibraryClassType {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .LibraryFunction {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .TagAttribute {
|
||||
}
|
||||
pre.idle .Keyword {
|
||||
color: #FF5600;
|
||||
}
|
||||
pre.idle .UserDefinedConstant {
|
||||
}
|
||||
pre.idle .String {
|
||||
color: #00A33F;
|
||||
}
|
||||
pre.idle .FunctionName {
|
||||
color: #21439C;
|
||||
}
|
||||
pre.idle .Variable {
|
||||
}
|
||||
pre.idle .Comment {
|
||||
color: #919191;
|
||||
}
|
||||
744
documentation/index.html.erb
Normal file
744
documentation/index.html.erb
Normal file
@@ -0,0 +1,744 @@
|
||||
<%
|
||||
require 'uv'
|
||||
def code_for(file, executable=false)
|
||||
@stripper ||= /(\A\(function\(\)\{\n|\}\)\(\);\Z|^ )/
|
||||
return '' unless File.exists?("documentation/js/#{file}.js")
|
||||
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)
|
||||
append = executable == true ? '' : "alert(#{executable});"
|
||||
run = executable == true ? 'run' : "run: #{executable}"
|
||||
button = executable ? "<button onclick='javascript: #{js};#{append}'>#{run}</button>" : ''
|
||||
"<div class='code'>#{cshtml}#{jshtml}#{button}<br class='clear' /></div>"
|
||||
end
|
||||
%>
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
||||
<title>CoffeeScript</title>
|
||||
<link rel="stylesheet" type="text/css" href="documentation/css/docs.css" />
|
||||
<link rel="stylesheet" type="text/css" href="documentation/css/idle.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<h1><sub style="font-size: 100px;">☕</sub> CoffeeScript</h1>
|
||||
|
||||
<p>
|
||||
CoffeeScript is a little language that compiles into JavaScript. Think
|
||||
of it as JavaScript's less ostentatious kid brother — the same genes,
|
||||
roughly the same height, but a different sense of style. Apart from a handful of
|
||||
bonus goodies, statements in CoffeeScript correspond one-to-one with their
|
||||
equivalent in JavaScript, it's just another way of saying it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Disclaimer:</b>
|
||||
CoffeeScript is just for fun and seriously alpha. I'm sure that there are still
|
||||
plenty of holes in the walls and leaks in the roof. <i>There are no guarantees
|
||||
that the syntax won't change between versions.</i> That said,
|
||||
it compiles into clean JavaScript (the good parts) that can use existing
|
||||
JavaScript libraries seamlessly, and passes through
|
||||
<a href="http://www.jslint.com/">JSLint</a> without warnings. The compiled
|
||||
output is quite readable — pretty-printed, with comments
|
||||
preserved intact.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Latest Version:</b>
|
||||
<a href="http://gemcutter.org/gems/coffee-script">0.2.4</a>
|
||||
</p>
|
||||
|
||||
<h2>Table of Contents</h2>
|
||||
|
||||
<p>
|
||||
<a href="#overview">Mini Overview</a><br />
|
||||
<a href="#installation">Installation and Usage</a><br />
|
||||
<a href="#whitespace">Significant Whitespace</a><br />
|
||||
<a href="#functions">Functions and Invocation</a><br />
|
||||
<a href="#assignment">Assignment</a><br />
|
||||
<a href="#objects_and_arrays">Objects and Arrays</a><br />
|
||||
<a href="#lexical_scope">Lexical Scoping and Variable Safety</a><br />
|
||||
<a href="#conditionals">Conditionals, Ternaries, and Conditional Assignment</a><br />
|
||||
<a href="#existence">The Existence Operator</a><br />
|
||||
<a href="#aliases">Aliases</a><br />
|
||||
<a href="#splats">Splats...</a><br />
|
||||
<a href="#arguments">Arguments are Arrays</a><br />
|
||||
<a href="#while">While Loops</a><br />
|
||||
<a href="#comprehensions">Comprehensions (Arrays, Objects, and Ranges)</a><br />
|
||||
<a href="#slice_splice">Array Slicing and Splicing with Ranges</a><br />
|
||||
<a href="#expressions">Everything is an Expression</a><br />
|
||||
<a href="#inheritance">Inheritance, and Calling Super from a Subclass</a><br />
|
||||
<a href="#blocks">Blocks</a><br />
|
||||
<a href="#pattern_matching">Pattern Matching</a><br />
|
||||
<a href="#embedded">Embedded JavaScript</a><br />
|
||||
<a href="#switch">Switch/When/Else</a><br />
|
||||
<a href="#try">Try/Catch/Finally</a><br />
|
||||
<a href="#strings">Multiline Strings and Heredocs</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>
|
||||
|
||||
<%= code_for('overview', 'cubed_list') %>
|
||||
|
||||
<p>
|
||||
For a longer CoffeeScript example, check out
|
||||
<a href="documentation/underscore.html">Underscore.coffee</a>, a port
|
||||
of the <a href="http://documentcloud.github.com/underscore/">Underscore.js</a>
|
||||
library of helper functions. Underscore.coffee can pass the entire Underscore.js
|
||||
test suite. The CoffeeScript version is faster than the original for a number
|
||||
of methods (in general, due to the speed of CoffeeScript's array comprehensions), and
|
||||
after being minified and gzipped, is only 241 bytes larger than the original
|
||||
JavaScript version.
|
||||
Additional examples are included in the source repository, inside the
|
||||
<a href="http://github.com/jashkenas/coffee-script/tree/master/examples/">examples</a> folder.
|
||||
</p>
|
||||
|
||||
<h2 id="installation">Installation and Usage</h2>
|
||||
|
||||
<p>
|
||||
The CoffeeScript compiler is written in pure Ruby, and is available
|
||||
as a Ruby Gem.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
gem install coffee-script</pre>
|
||||
|
||||
<p>
|
||||
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>-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 scripts 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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-w, --watch</code></td>
|
||||
<td>
|
||||
Watch the modification times of the coffee-scripts, recompiling as
|
||||
soon as a change occurs.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-p, --print</code></td>
|
||||
<td>
|
||||
Instead of writing out the JavaScript as a file, print it
|
||||
directly to <b>stdout</b>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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
|
||||
conjunction with <tt>--watch</tt>)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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 -e "square: x => x * x"</tt>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-t, --tokens</code></td>
|
||||
<td>
|
||||
Instead of parsing the CoffeeScript, just lex it, and print out the
|
||||
token stream: <tt>[:IDENTIFIER, "square"], [":", ":"], [:PARAM, "x"]</tt> ...
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-v, --verbose</code></td>
|
||||
<td>
|
||||
As the JavaScript is being generated, print out every step of code
|
||||
generation, including lexical scope and the node in the
|
||||
AST.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-n, --no-wrap</code></td>
|
||||
<td>
|
||||
Compile the JavaScript without the top-level function safety wrapper.
|
||||
(Used for CoffeeScript as a Narwhal module.)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>-g, --globals</code></td>
|
||||
<td>
|
||||
Suppress all variable declarations at the top-level, effectively adding
|
||||
those variables to the global scope. (Used by the REPL.)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--install-bundle</code></td>
|
||||
<td>
|
||||
Install the TextMate bundle for CoffeeScript syntax highlighting.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<b>Examples:</b>
|
||||
</p>
|
||||
|
||||
<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>
|
||||
|
||||
<p>
|
||||
<i>
|
||||
This reference is structured so that it can be read from top to bottom,
|
||||
if you like. Later sections use ideas and syntax previously introduced.
|
||||
Familiarity with JavaScript is assumed.
|
||||
In all of the following examples, the source CoffeeScript is provided on
|
||||
the left, and the direct compilation into JavaScript is on the right.
|
||||
</i>
|
||||
</p>
|
||||
|
||||
<p id="whitespace">
|
||||
<b class="header">Significant Whitespace</b>
|
||||
CoffeeScript uses Python-style significant whitespace: You don't need to
|
||||
use semicolons <tt>;</tt> to terminate expressions, ending
|
||||
the line will do just as well. Semicolons can still be used to fit
|
||||
multiple expressions onto a single line. Instead of using curly braces
|
||||
<tt>{ }</tt> to delimit blocks of code (like <a href="#functions">functions</a>,
|
||||
<a href="#conditionals">if-statements</a>,
|
||||
<a href="#switch">switch</a>, and <a href="#try">try/catch</a>),
|
||||
use indentation.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can use newlines to break up your expression into smaller pieces,
|
||||
as long as CoffeeScript can tell that the line hasn't finished
|
||||
(similar to how Ruby handles it). For example,
|
||||
if the line ends in an operator, dot, or keyword.
|
||||
</p>
|
||||
|
||||
<p id="functions">
|
||||
<b class="header">Functions and Invocation</b>
|
||||
Functions are defined by a list of parameters, an arrow, and the
|
||||
function body. The empty function looks like this: <tt>=></tt>. All
|
||||
functions in CoffeeScript are named, for the benefit of debug messages.
|
||||
</p>
|
||||
<%= code_for('functions', 'cube(5)') %>
|
||||
|
||||
<p id="assignment">
|
||||
<b class="header">Assignment</b>
|
||||
Use a colon <tt>:</tt> to assign, as in
|
||||
<a href="http://json.org">JSON</a>. Equal signs are only needed for
|
||||
mathy things.
|
||||
</p>
|
||||
<%= code_for('assignment', 'greeting') %>
|
||||
<p>
|
||||
Declarations of new variables are pushed up to the top of the nearest
|
||||
lexical scope, so that assignment may always be performed within expressions.
|
||||
</p>
|
||||
|
||||
<p id="objects_and_arrays">
|
||||
<b class="header">Objects and Arrays</b>
|
||||
Object and Array literals look very similar to their JavaScript cousins.
|
||||
When you spread out each assignment on a separate line, the commas are
|
||||
optional. In this way, assigning object properties looks the same as
|
||||
assigning local variables, and can be moved around freely. You can mix
|
||||
and match the two styles.
|
||||
</p>
|
||||
<%= code_for('objects_and_arrays', 'song.join(",")') %>
|
||||
|
||||
<p id="lexical_scope">
|
||||
<b class="header">Lexical Scoping and Variable Safety</b>
|
||||
The CoffeeScript compiler takes care to make sure that all of your variables
|
||||
are properly declared within lexical scope — you never need to write
|
||||
<tt>var</tt> yourself.
|
||||
</p>
|
||||
<%= code_for('scope', 'new_num') %>
|
||||
<p>
|
||||
Notice how the all of the variable declarations have been pushed up to
|
||||
the top of the closest scope, the first time they appear.
|
||||
<b>num</b> is not redeclared within the inner function, because it's
|
||||
already in scope; the <b>new_num</b> within the function, on the other hand,
|
||||
should not be able to change the value of the external variable of the same name, and
|
||||
therefore has a declaration of its own.
|
||||
</p>
|
||||
<p>
|
||||
Although suppressed within this documentation for clarity, all
|
||||
CoffeeScript output is wrapped in an anonymous function:
|
||||
<tt>(function(){ ... })();</tt> This safety wrapper, combined with the
|
||||
automatic generation of the <tt>var</tt> keyword, make it exceedingly difficult
|
||||
to pollute the global namespace by accident. If you'd like to create
|
||||
global variables, attach them as properties on <b>window</b>,
|
||||
or on the <b>exports</b> object in CommonJS.
|
||||
</p>
|
||||
|
||||
<p id="conditionals">
|
||||
<b class="header">Conditionals, Ternaries, and Conditional Assignment</b>
|
||||
<b>If/else</b> statements can be written without the use of parentheses and
|
||||
curly brackets. As with functions and other block expressions,
|
||||
multi-line conditionals are delimited by indentation. There's also a handy
|
||||
postfix form, with the <tt>if</tt> or <tt>unless</tt> at the end.
|
||||
</p>
|
||||
<p>
|
||||
CoffeeScript will compile <b>if</b> statements using the ternary operator
|
||||
when possible, to make it easier to use the result as an expression.
|
||||
</p>
|
||||
<%= code_for('conditionals') %>
|
||||
<p>
|
||||
The conditional assignment operators are included: <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
|
||||
truthy variables.
|
||||
</p>
|
||||
|
||||
<p id="existence">
|
||||
<b class="header">The Existence Operator</b>
|
||||
It's a little difficult to check for the existence of a variable in
|
||||
JavaScript. <tt>if (variable) ...</tt> comes close, but fails for zero,
|
||||
the empty string, and false. The existence operator <tt>?</tt> returns true unless
|
||||
a variable is <b>null</b> or <b>undefined</b>, which makes it analogous
|
||||
to Ruby's <tt>nil?</tt>
|
||||
</p>
|
||||
<%= code_for('existence') %>
|
||||
|
||||
<p id="aliases">
|
||||
<b class="header">Aliases</b>
|
||||
Because the <tt>==</tt> operator frequently causes undesirable coercion,
|
||||
is intransitive, and has a different meaning than in other languages,
|
||||
CoffeeScript compiles <tt>==</tt> into <tt>===</tt>, and <tt>!=</tt> into
|
||||
<tt>!==</tt>.
|
||||
In addition, <tt>is</tt> compiles into <tt>===</tt>,
|
||||
and <tt>isnt</tt> into <tt>!==</tt>.
|
||||
</p>
|
||||
<p>
|
||||
You can use <tt>not</tt> as an alias for <tt>!</tt>.
|
||||
</p>
|
||||
<p>
|
||||
For logic, <tt>and</tt> compiles to <tt>&&</tt>, and <tt>or</tt>
|
||||
into <tt>||</tt>.
|
||||
</p>
|
||||
<p>
|
||||
Instead of a newline or semicolon, <tt>then</tt> can be used to separate
|
||||
conditions from expressions, in <b>while</b>,
|
||||
<b>if</b>/<b>else</b>, and <b>switch</b>/<b>when</b> statements.
|
||||
</p>
|
||||
<p>
|
||||
As in <a href="http://yaml.org/">YAML</a>, <tt>on</tt> and <tt>yes</tt>
|
||||
are the same as boolean <tt>true</tt>, while <tt>off</tt> and <tt>no</tt> are boolean <tt>false</tt>.
|
||||
</p>
|
||||
<p>
|
||||
For single-line statements, <tt>unless</tt> can be used as the inverse of <tt>if</tt>.
|
||||
</p>
|
||||
<%= code_for('aliases') %>
|
||||
|
||||
<p id="splats">
|
||||
<b class="header">Splats...</b>
|
||||
The JavaScript <b>arguments object</b> is a useful way to work with
|
||||
functions that accept variable numbers of arguments. CoffeeScript provides
|
||||
splats <tt>...</tt>, both for function definition as well as invocation,
|
||||
making variable arguments a little bit more palatable.
|
||||
</p>
|
||||
<%= code_for('splats', true) %>
|
||||
|
||||
<p id="arguments">
|
||||
<b class="header">Arguments are Arrays</b>
|
||||
If you reference the <b>arguments object</b> directly, it will be converted
|
||||
into a real Array, making all of the
|
||||
<a href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array">Array methods</a>
|
||||
available.
|
||||
</p>
|
||||
<%= code_for('arguments', true) %>
|
||||
|
||||
<p id="while">
|
||||
<b class="header">While Loops</b>
|
||||
The only low-level loop that CoffeeScript provides is the while loop.
|
||||
</p>
|
||||
<%= code_for('while') %>
|
||||
<p>
|
||||
Other JavaScript loops, such as <b>for</b> loops and <b>do-while</b> loops
|
||||
can be mimicked by variations on <b>while</b>, but the hope is that you
|
||||
won't need to do that with CoffeeScript, either because you're using
|
||||
<b>each</b> (<b>forEach</b>) style iterators, or...
|
||||
</p>
|
||||
|
||||
<p id="comprehensions">
|
||||
<b class="header">Comprehensions (Arrays, Objects, and Ranges)</b>
|
||||
For your looping needs, CoffeeScript provides array comprehensions
|
||||
similar to Python's. They replace (and compile into) <b>for</b> loops, with
|
||||
optional guard clauses and the value of the current array index.
|
||||
Unlike for loops, array comprehensions are expressions, and can be returned
|
||||
and assigned. They should be able to handle most places where you otherwise
|
||||
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 know the start and end of your loop, or would like to step through
|
||||
in fixed-size increments, you can use a range to specify the start and
|
||||
end of your comprehension. (The long line-breaking "for" definitions in
|
||||
the compiled JS below allow ranges to count downwards, as well as upwards).
|
||||
</p>
|
||||
<%= code_for('range_comprehensions', 'countdown') %>
|
||||
<p>
|
||||
Comprehensions can also be used to iterate over the keys and values in
|
||||
an object. Use <tt>of</tt> to signal comprehension over the properties of
|
||||
an object instead of the values in an array.
|
||||
</p>
|
||||
<%= code_for('object_comprehensions', 'ages.join(", ")') %>
|
||||
|
||||
<p id="slice_splice">
|
||||
<b class="header">Array Slicing and Splicing 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', 'numbers_copy') %>
|
||||
<p>
|
||||
The same syntax can be used with assignment to replace a segment of an
|
||||
array with new values (to splice it).
|
||||
</p>
|
||||
<%= code_for('splices', 'numbers') %>
|
||||
|
||||
<p id="expressions">
|
||||
<b class="header">Everything is an Expression (at least, as much as possible)</b>
|
||||
You might have noticed how even though we don't add return statements
|
||||
to CoffeeScript functions, they nonetheless return their final value.
|
||||
The CoffeeScript compiler tries to make sure that all statements in the
|
||||
language can be used as expressions. Watch how the <tt>return</tt> gets
|
||||
pushed down into each possible branch of execution, in the function
|
||||
below.
|
||||
</p>
|
||||
<%= code_for('expressions', 'eldest') %>
|
||||
<p>
|
||||
Even though functions will always return their final value, it's both possible
|
||||
and encouraged to return early from a function body writing out the explicit
|
||||
return (<tt>return value</tt>), when you know that you're done.
|
||||
</p>
|
||||
<p>
|
||||
Because variable declarations occur at the top of scope, assignment can
|
||||
be used within expressions, even for variables that haven't been seen before:
|
||||
</p>
|
||||
<%= code_for('expressions_assignment', 'six') %>
|
||||
<p>
|
||||
Things that would otherwise be statements in JavaScript, when used
|
||||
as part of an expression in CoffeeScript, are converted into expressions
|
||||
by wrapping them in a closure. This lets you do useful things, like assign
|
||||
the result of a comprehension to a variable:
|
||||
</p>
|
||||
<%= code_for('expressions_comprehension', 'globals') %>
|
||||
<p>
|
||||
As well as silly things, like passing a <b>try/catch</b> statement directly
|
||||
into a function call:
|
||||
</p>
|
||||
<%= code_for('expressions_try', true) %>
|
||||
|
||||
<p id="inheritance">
|
||||
<b class="header">Inheritance, and Calling Super from a Subclass</b>
|
||||
JavaScript's prototypal inheritance has always been a bit of a
|
||||
brain-bender, with a whole family tree of libraries that provide a cleaner
|
||||
syntax for classical inheritance on top of JavaScript's prototypes:
|
||||
<a href="http://code.google.com/p/base2/">Base2</a>,
|
||||
<a href="http://prototypejs.org/">Prototype.js</a>,
|
||||
<a href="http://jsclass.jcoglan.com/">JS.Class</a>, etc.
|
||||
The libraries provide syntactic sugar, but the built-in inheritance would
|
||||
be completely usable if it weren't for a couple of small exceptions:
|
||||
it's awkward to call <b>super</b> (the prototype object's
|
||||
implementation of the current function), and it's awkward to correctly
|
||||
set the prototype chain.
|
||||
</p>
|
||||
<p>
|
||||
CoffeeScript provides <tt>extends</tt>
|
||||
to help with prototype setup, <tt>::</tt> for quick access to an
|
||||
object's prototype, and converts <tt>super()</tt> into a call against
|
||||
the immediate ancestor's method of the same name.
|
||||
</p>
|
||||
<%= code_for('super', true) %>
|
||||
|
||||
<p id="blocks">
|
||||
<b class="header">Blocks</b>
|
||||
Many common looping functions (in Prototype, jQuery, and Underscore,
|
||||
for example) take a single function as their final argument. To make
|
||||
final functions easier to pass, CoffeeScript includes block syntax,
|
||||
so you don't have to close the parentheses on the other side.
|
||||
</p>
|
||||
<%= code_for('blocks') %>
|
||||
<p>
|
||||
If you prefer not to use blocks, you'll need to add a pair of parentheses
|
||||
to help distinguish the arguments from the definition of the function:
|
||||
<tt>_.map(array, (num => num * 2))</tt>
|
||||
</p>
|
||||
|
||||
<p id="pattern_matching">
|
||||
<b class="header">Pattern Matching (Destructuring Assignment)</b>
|
||||
To make extracting values from complex arrays and objects more convenient,
|
||||
CoffeeScript implements ECMAScript Harmony's proposed
|
||||
<a href="http://wiki.ecmascript.org/doku.php?id=harmony:destructuring">destructuring assignment</a>
|
||||
syntax. When you assign an array or object literal to a value, CoffeeScript
|
||||
breaks up and matches both sides against each other, assigning the values
|
||||
on the right to the variables on the left. In the simplest case, it can be
|
||||
used for parallel assignment:
|
||||
</p>
|
||||
<%= code_for('parallel_assignment', 'bait') %>
|
||||
<p>
|
||||
But it's also helpful for dealing with functions that return multiple
|
||||
values.
|
||||
</p>
|
||||
<%= code_for('multiple_return_values', 'forecast') %>
|
||||
<p>
|
||||
Pattern matching can be used with any depth of array and object nesting,
|
||||
to help pull out deeply nested properties.
|
||||
</p>
|
||||
<%= code_for('object_extraction', 'poet + " — " + street') %>
|
||||
|
||||
<p id="embedded">
|
||||
<b class="header">Embedded JavaScript</b>
|
||||
Hopefully, you'll never need to use it, but if you ever need to intersperse
|
||||
snippets of JavaScript within your CoffeeScript, you can
|
||||
use backticks to pass it straight through.
|
||||
</p>
|
||||
<%= code_for('embedded', 'hi()') %>
|
||||
|
||||
<p id="switch">
|
||||
<b class="header">Switch/When/Else</b>
|
||||
<b>Switch</b> statements in JavaScript are rather broken. You can only
|
||||
do comparisons based on string equality, and need to remember to <b>break</b> at the end of
|
||||
every <b>case</b> statement to avoid accidentally falling through to
|
||||
the default case. CoffeeScript compiles <b>switch</b> statements into JavaScript if-else chains, allowing you to
|
||||
compare any object (via <b>===</b>), preventing fall-through, and resulting
|
||||
in a returnable, assignable expression. The format is: <tt>switch</tt> condition,
|
||||
<tt>when</tt> clauses, <tt>else</tt> the default case.
|
||||
</p>
|
||||
<%= code_for('switch') %>
|
||||
|
||||
<p id="try">
|
||||
<b class="header">Try/Catch/Finally</b>
|
||||
Try/catch statements are just about the same as JavaScript (although
|
||||
they work as expressions).
|
||||
</p>
|
||||
<%= code_for('try') %>
|
||||
|
||||
<p id="strings">
|
||||
<b class="header">Multiline Strings and Heredocs</b>
|
||||
Multiline strings are allowed in CoffeeScript.
|
||||
</p>
|
||||
<%= code_for('strings', 'moby_dick') %>
|
||||
<p>
|
||||
Heredocs can be used to hold formatted or indentation-sensitive text
|
||||
(or, if you just don't feel like escaping quotes and apostrophes). The
|
||||
indentation level that begins the heredoc is maintained throughout, so
|
||||
you can keep it all aligned with the body of your code.
|
||||
</p>
|
||||
<%= code_for('heredocs') %>
|
||||
|
||||
<h2 id="resources">Resources</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://github.com/jashkenas/coffee-script/">Source Code</a><br />
|
||||
After checking out the source, make sure to run <tt>rake build:parser</tt>
|
||||
to generate an up-to-date version of the Racc parser.
|
||||
Use <tt>bin/coffee</tt> to test your changes,
|
||||
<tt>rake test</tt> to run the test suite,
|
||||
and <tt>rake gem:install</tt> to
|
||||
create and install a custom version of the gem.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://github.com/jashkenas/coffee-script/issues">Bugs, Feature Requests, and General Discussion</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://github.com/jnicklas/bistro_car">BistroCar</a><br />
|
||||
A Rails plugin by
|
||||
<a href="http://github.com/jnicklas">Jonas Nicklas</a>
|
||||
that includes CoffeeScript helpers,
|
||||
bundling and minification.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="contributing">Contributing</h2>
|
||||
|
||||
<p>
|
||||
Here's a wish list of things that would be wonderful to have contributed:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://github.com/jashkenas/coffee-script/issues#issue/8">
|
||||
A CoffeeScript version of the compiler.
|
||||
</a>
|
||||
</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
|
||||
without knowledge of JavaScript.
|
||||
</li>
|
||||
<li>
|
||||
Integration with Processing.js's JavaScript API (this would depend on
|
||||
having a JavaScript version of the compiler).
|
||||
</li>
|
||||
<li>
|
||||
A lot of the code generation in <tt>nodes.rb</tt> gets into messy
|
||||
string manipulation. Techniques for cleaning this up across the board
|
||||
would be appreciated.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="change_log">Change Log</h2>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.4</b>
|
||||
Added ECMAScript Harmony style destructuring assignment, for dealing with
|
||||
extracting values from nested arrays and objects. Added indentation-sensitive
|
||||
heredocs for nicely formatted strings or chunks of code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.3</b>
|
||||
Axed the unsatisfactory <tt>ino</tt> keyword, replacing it with <tt>of</tt> for
|
||||
object comprehensions. They now look like: <tt>for prop, value of object</tt>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.2</b>
|
||||
When performing a comprehension over an object, use <tt>ino</tt>, instead
|
||||
of <tt>in</tt>, which helps us generate smaller, more efficient code at
|
||||
compile time.
|
||||
<br />
|
||||
Added <tt>::</tt> as a shorthand for saying <tt>.prototype.</tt>
|
||||
<br />
|
||||
The "splat" symbol has been changed from a prefix asterisk <tt>*</tt>, to
|
||||
a postfix ellipsis <tt>...</tt>
|
||||
<br />
|
||||
Added JavaScript's <tt>in</tt> operator,
|
||||
empty <tt>return</tt> statements, and empty <tt>while</tt> loops.
|
||||
<br />
|
||||
Constructor functions that start with capital letters now include a
|
||||
safety check to make sure that the new instance of the object is returned.
|
||||
<br />
|
||||
The <tt>extends</tt> keyword now functions identically to <tt>goog.inherits</tt>
|
||||
in Google's Closure Library.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.1</b>
|
||||
Arguments objects are now converted into real arrays when referenced.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.0</b>
|
||||
Major release. Significant whitespace. Better statement-to-expression
|
||||
conversion. Splats. Splice literals. Object comprehensions. Blocks.
|
||||
The existence operator. Many thanks to all the folks who posted issues,
|
||||
with special thanks to
|
||||
<a href="http://github.com/kamatsu">Liam O'Connor-Davis</a> for whitespace
|
||||
and expression help.
|
||||
</p>
|
||||
|
||||
<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
|
||||
inheritance, with the re-addition of the <tt>extends</tt> keyword.
|
||||
Added experimental <a href="http://narwhaljs.org/">Narwhal</a>
|
||||
support (as a Tusk package), contributed by
|
||||
<a href="http://tlrobinson.net/">Tom Robinson</a>, including
|
||||
<b>bin/cs</b> as a CoffeeScript REPL and interpreter.
|
||||
New <tt>--no-wrap</tt> option to suppress the safety function
|
||||
wrapper.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.1.1</b>
|
||||
Added <tt>instanceof</tt> and <tt>typeof</tt> as operators.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.1.0</b>
|
||||
Initial CoffeeScript release.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
13
documentation/js/aliases.js
Normal file
13
documentation/js/aliases.js
Normal file
@@ -0,0 +1,13 @@
|
||||
(function(){
|
||||
var volume;
|
||||
if (ignition === true) {
|
||||
launch();
|
||||
}
|
||||
if (band !== spinal_tap) {
|
||||
volume = 10;
|
||||
}
|
||||
if (!(answer === false)) {
|
||||
let_the_wild_rumpus_begin();
|
||||
}
|
||||
car.speed < speed_limit ? accelerate() : null;
|
||||
})();
|
||||
7
documentation/js/arguments.js
Normal file
7
documentation/js/arguments.js
Normal file
@@ -0,0 +1,7 @@
|
||||
(function(){
|
||||
var backwards;
|
||||
backwards = function backwards() {
|
||||
return alert(Array.prototype.slice.call(arguments, 0).reverse());
|
||||
};
|
||||
backwards("stairway", "to", "heaven");
|
||||
})();
|
||||
26
documentation/js/array_comprehensions.js
Normal file
26
documentation/js/array_comprehensions.js
Normal file
@@ -0,0 +1,26 @@
|
||||
(function(){
|
||||
var __a, __b, __c, __d, __e, __f, __g, food, lunch, roid, roid2;
|
||||
// Eat lunch.
|
||||
lunch = (function() {
|
||||
__a = []; __b = ['toast', 'cheese', 'wine'];
|
||||
for (__c=0; __c<__b.length; __c++) {
|
||||
food = __b[__c];
|
||||
__a.push(eat(food));
|
||||
}
|
||||
return __a;
|
||||
})();
|
||||
// Naive collision detection.
|
||||
__d = asteroids;
|
||||
for (__e=0; __e<__d.length; __e++) {
|
||||
roid = __d[__e];
|
||||
__f = asteroids;
|
||||
for (__g=0; __g<__f.length; __g++) {
|
||||
roid2 = __f[__g];
|
||||
if (roid !== roid2) {
|
||||
if (roid.overlaps(roid2)) {
|
||||
roid.explode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
5
documentation/js/assignment.js
Normal file
5
documentation/js/assignment.js
Normal file
@@ -0,0 +1,5 @@
|
||||
(function(){
|
||||
var difficulty, greeting;
|
||||
greeting = "Hello CoffeeScript";
|
||||
difficulty = 0.5;
|
||||
})();
|
||||
8
documentation/js/blocks.js
Normal file
8
documentation/js/blocks.js
Normal file
@@ -0,0 +1,8 @@
|
||||
(function(){
|
||||
$('table.list').each(function(table) {
|
||||
return $('tr.account', table).each(function(row) {
|
||||
row.show();
|
||||
return row.highlight();
|
||||
});
|
||||
});
|
||||
})();
|
||||
12
documentation/js/conditionals.js
Normal file
12
documentation/js/conditionals.js
Normal file
@@ -0,0 +1,12 @@
|
||||
(function(){
|
||||
var date, mood;
|
||||
if (singing) {
|
||||
mood = greatly_improved;
|
||||
}
|
||||
if (happy && knows_it) {
|
||||
claps_hands();
|
||||
cha_cha_cha();
|
||||
}
|
||||
date = friday ? sue : jill;
|
||||
expensive = expensive || do_the_math();
|
||||
})();
|
||||
6
documentation/js/embedded.js
Normal file
6
documentation/js/embedded.js
Normal file
@@ -0,0 +1,6 @@
|
||||
(function(){
|
||||
var hi;
|
||||
hi = function() {
|
||||
return [document.title, "Hello JavaScript"].join(": ");
|
||||
};
|
||||
})();
|
||||
6
documentation/js/existence.js
Normal file
6
documentation/js/existence.js
Normal file
@@ -0,0 +1,6 @@
|
||||
(function(){
|
||||
var solipsism;
|
||||
if ((typeof mind !== "undefined" && mind !== null) && !(typeof world !== "undefined" && world !== null)) {
|
||||
solipsism = true;
|
||||
}
|
||||
})();
|
||||
13
documentation/js/expressions.js
Normal file
13
documentation/js/expressions.js
Normal file
@@ -0,0 +1,13 @@
|
||||
(function(){
|
||||
var eldest, grade;
|
||||
grade = function grade(student) {
|
||||
if (student.excellent_work) {
|
||||
return "A+";
|
||||
} else if (student.okay_stuff) {
|
||||
return student.tried_hard ? "B" : "B-";
|
||||
} else {
|
||||
return "C";
|
||||
}
|
||||
};
|
||||
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);
|
||||
})();
|
||||
13
documentation/js/expressions_comprehension.js
Normal file
13
documentation/js/expressions_comprehension.js
Normal file
@@ -0,0 +1,13 @@
|
||||
(function(){
|
||||
var __a, __b, globals, name;
|
||||
// The first ten global properties.
|
||||
globals = ((function() {
|
||||
__a = []; __b = window;
|
||||
for (name in __b) {
|
||||
if (__b.hasOwnProperty(name)) {
|
||||
__a.push(name);
|
||||
}
|
||||
}
|
||||
return __a;
|
||||
})()).slice(0, 10);
|
||||
})();
|
||||
9
documentation/js/expressions_try.js
Normal file
9
documentation/js/expressions_try.js
Normal file
@@ -0,0 +1,9 @@
|
||||
(function(){
|
||||
alert((function() {
|
||||
try {
|
||||
return nonexistent / undefined;
|
||||
} catch (error) {
|
||||
return "Caught an error: " + error;
|
||||
}
|
||||
})());
|
||||
})();
|
||||
9
documentation/js/functions.js
Normal file
9
documentation/js/functions.js
Normal file
@@ -0,0 +1,9 @@
|
||||
(function(){
|
||||
var cube, square;
|
||||
square = function square(x) {
|
||||
return x * x;
|
||||
};
|
||||
cube = function cube(x) {
|
||||
return square(x) * x;
|
||||
};
|
||||
})();
|
||||
4
documentation/js/heredocs.js
Normal file
4
documentation/js/heredocs.js
Normal file
@@ -0,0 +1,4 @@
|
||||
(function(){
|
||||
var html;
|
||||
html = "<strong>\n cup of coffeescript\n</strong>";
|
||||
})();
|
||||
7
documentation/js/intro.js
Normal file
7
documentation/js/intro.js
Normal file
@@ -0,0 +1,7 @@
|
||||
(function(){
|
||||
|
||||
// CoffeeScript on the left, JS on the right.
|
||||
var square = function(x) {
|
||||
return x * x;
|
||||
};
|
||||
})();
|
||||
11
documentation/js/multiple_return_values.js
Normal file
11
documentation/js/multiple_return_values.js
Normal file
@@ -0,0 +1,11 @@
|
||||
(function(){
|
||||
var __a, city, forecast, temp, weather_report;
|
||||
weather_report = function weather_report(location) {
|
||||
// Make an Ajax request to fetch the weather...
|
||||
return [location, 72, "Mostly Sunny"];
|
||||
};
|
||||
__a = weather_report("Berkeley, CA");
|
||||
city = __a[0];
|
||||
temp = __a[1];
|
||||
forecast = __a[2];
|
||||
})();
|
||||
18
documentation/js/object_comprehensions.js
Normal file
18
documentation/js/object_comprehensions.js
Normal file
@@ -0,0 +1,18 @@
|
||||
(function(){
|
||||
var __a, __b, age, ages, child, years_old;
|
||||
years_old = {
|
||||
max: 10,
|
||||
ida: 9,
|
||||
tim: 11
|
||||
};
|
||||
ages = (function() {
|
||||
__a = []; __b = years_old;
|
||||
for (child in __b) {
|
||||
age = __b[child];
|
||||
if (__b.hasOwnProperty(child)) {
|
||||
__a.push(child + " is " + age);
|
||||
}
|
||||
}
|
||||
return __a;
|
||||
})();
|
||||
})();
|
||||
17
documentation/js/object_extraction.js
Normal file
17
documentation/js/object_extraction.js
Normal file
@@ -0,0 +1,17 @@
|
||||
(function(){
|
||||
var __a, __b, __c, city, futurists, poet, street;
|
||||
futurists = {
|
||||
sculptor: "Umberto Boccioni",
|
||||
painter: "Vladimir Burliuk",
|
||||
poet: {
|
||||
name: "F.T. Marinetti",
|
||||
address: ["Via Roma 42R", "Bellagio, Italy 22021"]
|
||||
}
|
||||
};
|
||||
__a = futurists;
|
||||
__b = __a.poet;
|
||||
poet = __b.name;
|
||||
__c = __b.address;
|
||||
street = __c[0];
|
||||
city = __c[1];
|
||||
})();
|
||||
10
documentation/js/objects_and_arrays.js
Normal file
10
documentation/js/objects_and_arrays.js
Normal file
@@ -0,0 +1,10 @@
|
||||
(function(){
|
||||
var ages, matrix, song;
|
||||
song = ["do", "re", "mi", "fa", "so"];
|
||||
ages = {
|
||||
max: 10,
|
||||
ida: 9,
|
||||
tim: 11
|
||||
};
|
||||
matrix = [1, 0, 1, 0, 0, 1, 1, 1, 0];
|
||||
})();
|
||||
43
documentation/js/overview.js
Normal file
43
documentation/js/overview.js
Normal file
@@ -0,0 +1,43 @@
|
||||
(function(){
|
||||
var __a, __b, __c, cubed_list, list, math, num, number, opposite_day, race, square;
|
||||
// Assignment:
|
||||
number = 42;
|
||||
opposite_day = true;
|
||||
// Conditions:
|
||||
if (opposite_day) {
|
||||
number = -42;
|
||||
}
|
||||
// Functions:
|
||||
square = function square(x) {
|
||||
return x * x;
|
||||
};
|
||||
// Arrays:
|
||||
list = [1, 2, 3, 4, 5];
|
||||
// Objects:
|
||||
math = {
|
||||
root: Math.sqrt,
|
||||
square: square,
|
||||
cube: function cube(x) {
|
||||
return x * square(x);
|
||||
}
|
||||
};
|
||||
// Splats:
|
||||
race = function race(winner) {
|
||||
var runners;
|
||||
runners = Array.prototype.slice.call(arguments, 1);
|
||||
return print(winner, runners);
|
||||
};
|
||||
// Existence:
|
||||
if ((typeof elvis !== "undefined" && elvis !== null)) {
|
||||
alert("I knew it!");
|
||||
}
|
||||
// Array comprehensions:
|
||||
cubed_list = (function() {
|
||||
__a = []; __b = list;
|
||||
for (__c=0; __c<__b.length; __c++) {
|
||||
num = __b[__c];
|
||||
__a.push(math.cube(num));
|
||||
}
|
||||
return __a;
|
||||
})();
|
||||
})();
|
||||
8
documentation/js/parallel_assignment.js
Normal file
8
documentation/js/parallel_assignment.js
Normal file
@@ -0,0 +1,8 @@
|
||||
(function(){
|
||||
var __a, and_switch, bait;
|
||||
bait = 1000;
|
||||
and_switch = 0;
|
||||
__a = [and_switch, bait];
|
||||
bait = __a[0];
|
||||
and_switch = __a[1];
|
||||
})();
|
||||
8
documentation/js/punctuation.js
Normal file
8
documentation/js/punctuation.js
Normal file
@@ -0,0 +1,8 @@
|
||||
(function(){
|
||||
|
||||
// Comments start with hash marks. Periods mark the end of a block.
|
||||
var left_hand = raining ? umbrella : parasol;
|
||||
// To signal the beginning of the next expression,
|
||||
// use "then", or a newline.
|
||||
left_hand = raining ? umbrella : parasol;
|
||||
})();
|
||||
21
documentation/js/range_comprehensions.js
Normal file
21
documentation/js/range_comprehensions.js
Normal file
@@ -0,0 +1,21 @@
|
||||
(function(){
|
||||
var __a, __b, __c, __d, __e, countdown, egg_delivery, num;
|
||||
countdown = (function() {
|
||||
__a = []; __d = 10; __e = 1;
|
||||
for (__c=0, num=__d; (__d <= __e ? num <= __e : num >= __e); (__d <= __e ? num += 1 : num -= 1), __c++) {
|
||||
__a.push(num);
|
||||
}
|
||||
return __a;
|
||||
})();
|
||||
egg_delivery = function egg_delivery() {
|
||||
var __f, __g, __h, __i, __j, dozen_eggs, i;
|
||||
__f = []; __i = 0; __j = eggs.length;
|
||||
for (__h=0, i=__i; (__i <= __j ? i < __j : i > __j); (__i <= __j ? i += 12 : i -= 12), __h++) {
|
||||
__f.push((function() {
|
||||
dozen_eggs = eggs.slice(i, i + 12);
|
||||
return deliver(new egg_carton(dozen));
|
||||
})());
|
||||
}
|
||||
return __f;
|
||||
};
|
||||
})();
|
||||
10
documentation/js/scope.js
Normal file
10
documentation/js/scope.js
Normal file
@@ -0,0 +1,10 @@
|
||||
(function(){
|
||||
var change_numbers, new_num, num;
|
||||
num = 1;
|
||||
change_numbers = function change_numbers() {
|
||||
var new_num;
|
||||
new_num = -1;
|
||||
return num = 10;
|
||||
};
|
||||
new_num = change_numbers();
|
||||
})();
|
||||
6
documentation/js/slices.js
Normal file
6
documentation/js/slices.js
Normal file
@@ -0,0 +1,6 @@
|
||||
(function(){
|
||||
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);
|
||||
})();
|
||||
16
documentation/js/splats.js
Normal file
16
documentation/js/splats.js
Normal file
@@ -0,0 +1,16 @@
|
||||
(function(){
|
||||
var contenders, gold, medalists, silver, the_field;
|
||||
gold = silver = the_field = "unknown";
|
||||
medalists = function medalists(first, second) {
|
||||
var rest;
|
||||
rest = Array.prototype.slice.call(arguments, 2);
|
||||
gold = first;
|
||||
silver = second;
|
||||
return the_field = rest;
|
||||
};
|
||||
contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];
|
||||
medalists.apply(this, contenders);
|
||||
alert("Gold: " + gold);
|
||||
alert("Silver: " + silver);
|
||||
alert("The Field: " + the_field);
|
||||
})();
|
||||
5
documentation/js/splices.js
Normal file
5
documentation/js/splices.js
Normal file
@@ -0,0 +1,5 @@
|
||||
(function(){
|
||||
var numbers;
|
||||
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
numbers.splice.apply(numbers, [3, 6 - 3 + 1].concat([-3, -4, -5, -6]));
|
||||
})();
|
||||
9
documentation/js/strings.js
Normal file
9
documentation/js/strings.js
Normal file
@@ -0,0 +1,9 @@
|
||||
(function(){
|
||||
var moby_dick;
|
||||
moby_dick = "Call me Ishmael. Some years ago -- \
|
||||
never mind how long precisely -- having little \
|
||||
or no money in my purse, and nothing particular \
|
||||
to interest me on shore, I thought I would sail \
|
||||
about a little and see the watery part of the \
|
||||
world...";
|
||||
})();
|
||||
40
documentation/js/super.js
Normal file
40
documentation/js/super.js
Normal file
@@ -0,0 +1,40 @@
|
||||
(function(){
|
||||
var Animal, Horse, Snake, __a, __b, sam, tom;
|
||||
Animal = function Animal() {
|
||||
};
|
||||
Animal.prototype.move = function move(meters) {
|
||||
return alert(this.name + " moved " + meters + "m.");
|
||||
};
|
||||
Snake = function Snake(name) {
|
||||
var __a;
|
||||
__a = this.name = name;
|
||||
return Snake === this.constructor ? this : __a;
|
||||
};
|
||||
__a = function(){};
|
||||
__a.prototype = Animal.prototype;
|
||||
Snake.__superClass__ = Animal.prototype;
|
||||
Snake.prototype = new __a();
|
||||
Snake.prototype.constructor = Snake;
|
||||
Snake.prototype.move = function move() {
|
||||
alert("Slithering...");
|
||||
return Snake.__superClass__.move.call(this, 5);
|
||||
};
|
||||
Horse = function Horse(name) {
|
||||
var __b;
|
||||
__b = this.name = name;
|
||||
return Horse === this.constructor ? this : __b;
|
||||
};
|
||||
__b = function(){};
|
||||
__b.prototype = Animal.prototype;
|
||||
Horse.__superClass__ = Animal.prototype;
|
||||
Horse.prototype = new __b();
|
||||
Horse.prototype.constructor = Horse;
|
||||
Horse.prototype.move = function move() {
|
||||
alert("Galloping...");
|
||||
return Horse.__superClass__.move.call(this, 45);
|
||||
};
|
||||
sam = new Snake("Sammy the Python");
|
||||
tom = new Horse("Tommy the Palomino");
|
||||
sam.move();
|
||||
tom.move();
|
||||
})();
|
||||
16
documentation/js/switch.js
Normal file
16
documentation/js/switch.js
Normal file
@@ -0,0 +1,16 @@
|
||||
(function(){
|
||||
if (day === "Tuesday") {
|
||||
eat_breakfast();
|
||||
} else if (day === "Wednesday") {
|
||||
go_to_the_park();
|
||||
} else if (day === "Saturday") {
|
||||
if (day === bingo_day) {
|
||||
go_to_bingo();
|
||||
go_dancing();
|
||||
}
|
||||
} else if (day === "Sunday") {
|
||||
go_to_church();
|
||||
} else {
|
||||
go_to_work();
|
||||
}
|
||||
})();
|
||||
10
documentation/js/try.js
Normal file
10
documentation/js/try.js
Normal file
@@ -0,0 +1,10 @@
|
||||
(function(){
|
||||
try {
|
||||
all_hell_breaks_loose();
|
||||
cats_and_dogs_living_together();
|
||||
} catch (error) {
|
||||
print(error);
|
||||
} finally {
|
||||
clean_up();
|
||||
}
|
||||
})();
|
||||
9
documentation/js/while.js
Normal file
9
documentation/js/while.js
Normal file
@@ -0,0 +1,9 @@
|
||||
(function(){
|
||||
while (demand > supply) {
|
||||
sell();
|
||||
restock();
|
||||
}
|
||||
while (supply > demand) {
|
||||
buy();
|
||||
}
|
||||
})();
|
||||
77
documentation/speed.html
Normal file
77
documentation/speed.html
Normal file
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Quickie CoffeeScript Speed Tests</title>
|
||||
<script type="text/javascript" src="http://www.broofa.com/Tools/JSLitmus/JSLitmus.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Quickie CoffeeScript Speed Tests</h1>
|
||||
|
||||
<script type="text/javascript">
|
||||
var num = 1000;
|
||||
var arr = [];
|
||||
while (num--) arr.push(num);
|
||||
|
||||
JSLitmus.test('current comprehensions', function() {
|
||||
__a = arr;
|
||||
__c = [];
|
||||
for (__b in __a) {
|
||||
if (__a.hasOwnProperty(__b)) {
|
||||
num = __a[__b];
|
||||
__d = num;
|
||||
__c.push(num);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JSLitmus.test('raw for loop (best we can do)', function() {
|
||||
__a = arr;
|
||||
__c = new Array(__a.length);
|
||||
for (__b=0; __b < __a.length; __b++) {
|
||||
__c[__b] = __a[__b];
|
||||
}
|
||||
});
|
||||
|
||||
JSLitmus.test('current without hasOwnProperty check', function() {
|
||||
__a = arr;
|
||||
__c = [];
|
||||
for (__b in __a) {
|
||||
num = __a[__b];
|
||||
__d = num;
|
||||
__c.push(num);
|
||||
}
|
||||
});
|
||||
|
||||
JSLitmus.test('raw for..in loop', function() {
|
||||
__a = arr;
|
||||
__c = new Array(__a.length);
|
||||
for (__b in __a) {
|
||||
__c[__b] = __a[__b];
|
||||
}
|
||||
});
|
||||
|
||||
JSLitmus.test('weepy\'s comprehensions', function() {
|
||||
__c = []; __a = arr;
|
||||
__d = function(num, __b) {
|
||||
__c.push(num);
|
||||
};
|
||||
if (__a instanceof Array) {
|
||||
for (__b=0; __b<__a.length; __b++) __d(__a[__b], __b);
|
||||
} else {
|
||||
for (__b in __a) { if (__a.hasOwnProperty(__b)) __d(__a[__b], __b); }
|
||||
}
|
||||
});
|
||||
|
||||
JSLitmus.test('CoffeeScript 0.2.2 comprehensions', function() {
|
||||
__c = []; __a = arr;
|
||||
for (__b=0; __b<__a.length; __b++) {
|
||||
num = __a[__b];
|
||||
__c.push(num);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
627
documentation/underscore.html
Normal file
627
documentation/underscore.html
Normal file
@@ -0,0 +1,627 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||
<link rel="stylesheet" type="text/css" media="screen,projection,print" href="css/idle.css" />
|
||||
<title>Underscore.coffee</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
pre.idle {
|
||||
font-family: "Monaco", "Consolas", monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<pre class="idle"><span class="line-numbers"> 1 </span>
|
||||
<span class="line-numbers"> 2 </span> <span class="Comment"><span class="Comment">#</span> Underscore.coffee</span>
|
||||
<span class="line-numbers"> 3 </span> <span class="Comment"><span class="Comment">#</span> (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.</span>
|
||||
<span class="line-numbers"> 4 </span> <span class="Comment"><span class="Comment">#</span> Underscore is freely distributable under the terms of the MIT license.</span>
|
||||
<span class="line-numbers"> 5 </span> <span class="Comment"><span class="Comment">#</span> Portions of Underscore are inspired by or borrowed from Prototype.js,</span>
|
||||
<span class="line-numbers"> 6 </span> <span class="Comment"><span class="Comment">#</span> Oliver Steele's Functional, and John Resig's Micro-Templating.</span>
|
||||
<span class="line-numbers"> 7 </span> <span class="Comment"><span class="Comment">#</span> For all details and documentation:</span>
|
||||
<span class="line-numbers"> 8 </span> <span class="Comment"><span class="Comment">#</span> http://documentcloud.github.com/underscore/</span>
|
||||
<span class="line-numbers"> 9 </span>
|
||||
<span class="line-numbers"> 10 </span>
|
||||
<span class="line-numbers"> 11 </span> <span class="Comment"><span class="Comment">#</span> ------------------------- Baseline setup ---------------------------------</span>
|
||||
<span class="line-numbers"> 12 </span>
|
||||
<span class="line-numbers"> 13 </span> <span class="Comment"><span class="Comment">#</span> Establish the root object, "window" in the browser, or "global" on the server.</span>
|
||||
<span class="line-numbers"> 14 </span> <span class="FunctionName">root</span><span class="Keyword">:</span> <span class="Variable">this</span>
|
||||
<span class="line-numbers"> 15 </span>
|
||||
<span class="line-numbers"> 16 </span>
|
||||
<span class="line-numbers"> 17 </span> <span class="Comment"><span class="Comment">#</span> Save the previous value of the "_" variable.</span>
|
||||
<span class="line-numbers"> 18 </span> <span class="FunctionName">previousUnderscore</span><span class="Keyword">:</span> root._
|
||||
<span class="line-numbers"> 19 </span>
|
||||
<span class="line-numbers"> 20 </span>
|
||||
<span class="line-numbers"> 21 </span> <span class="Comment"><span class="Comment">#</span> If Underscore is called as a function, it returns a wrapped object that</span>
|
||||
<span class="line-numbers"> 22 </span> <span class="Comment"><span class="Comment">#</span> can be used OO-style. This wrapper holds altered versions of all the</span>
|
||||
<span class="line-numbers"> 23 </span> <span class="Comment"><span class="Comment">#</span> underscore functions. Wrapped objects may be chained.</span>
|
||||
<span class="line-numbers"> 24 </span> <span class="FunctionArgument"> wrapper: obj </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 25 </span> <span class="FunctionName">this._wrapped</span><span class="Keyword">:</span> obj
|
||||
<span class="line-numbers"> 26 </span> <span class="Variable">this</span>
|
||||
<span class="line-numbers"> 27 </span>
|
||||
<span class="line-numbers"> 28 </span>
|
||||
<span class="line-numbers"> 29 </span> <span class="Comment"><span class="Comment">#</span> Establish the object that gets thrown to break out of a loop iteration.</span>
|
||||
<span class="line-numbers"> 30 </span> <span class="FunctionName">breaker</span><span class="Keyword">:</span> <span class="Keyword">if</span> <span class="Keyword">typeof</span>(StopIteration) <span class="Keyword">is</span> <span class="String"><span class="String">'</span>undefined<span class="String">'</span></span> <span class="Keyword">then</span> <span class="String"><span class="String">'</span>__break__<span class="String">'</span></span> <span class="Keyword">else</span> StopIteration
|
||||
<span class="line-numbers"> 31 </span>
|
||||
<span class="line-numbers"> 32 </span>
|
||||
<span class="line-numbers"> 33 </span> <span class="Comment"><span class="Comment">#</span> Create a safe reference to the Underscore object forreference below.</span>
|
||||
<span class="line-numbers"> 34 </span> <span class="FunctionArgument"> _: root._: obj </span><span class="Storage">=></span> <span class="Keyword">new</span> <span class="TypeName">wrapper</span>(obj)
|
||||
<span class="line-numbers"> 35 </span>
|
||||
<span class="line-numbers"> 36 </span>
|
||||
<span class="line-numbers"> 37 </span> <span class="Comment"><span class="Comment">#</span> Export the Underscore object for CommonJS.</span>
|
||||
<span class="line-numbers"> 38 </span> <span class="Keyword">if</span> <span class="Keyword">typeof</span>(exports) <span class="Keyword">!</span><span class="Keyword">=</span> <span class="String"><span class="String">'</span>undefined<span class="String">'</span></span> <span class="Keyword">then</span> <span class="FunctionName">exports._</span><span class="Keyword">:</span> _
|
||||
<span class="line-numbers"> 39 </span>
|
||||
<span class="line-numbers"> 40 </span>
|
||||
<span class="line-numbers"> 41 </span> <span class="Comment"><span class="Comment">#</span> Create quick reference variables for speed access to core prototypes.</span>
|
||||
<span class="line-numbers"> 42 </span> <span class="FunctionName">slice</span><span class="Keyword">:</span> Array<span class="Keyword">:</span><span class="Keyword">:</span>slice
|
||||
<span class="line-numbers"> 43 </span> <span class="FunctionName">unshift</span><span class="Keyword">:</span> Array<span class="Keyword">:</span><span class="Keyword">:</span>unshift
|
||||
<span class="line-numbers"> 44 </span> <span class="FunctionName">toString</span><span class="Keyword">:</span> Object<span class="Keyword">:</span><span class="Keyword">:</span>toString
|
||||
<span class="line-numbers"> 45 </span> <span class="FunctionName">hasOwnProperty</span><span class="Keyword">:</span> Object<span class="Keyword">:</span><span class="Keyword">:</span>hasOwnProperty
|
||||
<span class="line-numbers"> 46 </span> <span class="FunctionName">propertyIsEnumerable</span><span class="Keyword">:</span> Object<span class="Keyword">:</span><span class="Keyword">:</span>propertyIsEnumerable
|
||||
<span class="line-numbers"> 47 </span>
|
||||
<span class="line-numbers"> 48 </span>
|
||||
<span class="line-numbers"> 49 </span> <span class="Comment"><span class="Comment">#</span> Current version.</span>
|
||||
<span class="line-numbers"> 50 </span> <span class="FunctionName">_.VERSION</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span>0.5.5<span class="String">'</span></span>
|
||||
<span class="line-numbers"> 51 </span>
|
||||
<span class="line-numbers"> 52 </span>
|
||||
<span class="line-numbers"> 53 </span> <span class="Comment"><span class="Comment">#</span> ------------------------ Collection Functions: ---------------------------</span>
|
||||
<span class="line-numbers"> 54 </span>
|
||||
<span class="line-numbers"> 55 </span> <span class="Comment"><span class="Comment">#</span> The cornerstone, an each implementation.</span>
|
||||
<span class="line-numbers"> 56 </span> <span class="Comment"><span class="Comment">#</span> Handles objects implementing forEach, arrays, and raw objects.</span>
|
||||
<span class="line-numbers"> 57 </span> <span class="FunctionArgument"> _.each: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 58 </span> <span class="FunctionName">index</span><span class="Keyword">:</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 59 </span> <span class="Keyword">try</span>
|
||||
<span class="line-numbers"> 60 </span> <span class="Keyword">return</span> obj.forEach(iterator, context) <span class="Keyword">if</span> obj.forEach
|
||||
<span class="line-numbers"> 61 </span> <span class="Keyword">if</span> _.isArray(obj) <span class="Keyword">or</span> _.isArguments(obj)
|
||||
<span class="line-numbers"> 62 </span> <span class="Keyword">return</span> iterator.call(context, obj[i], i, obj) <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...obj.length]
|
||||
<span class="line-numbers"> 63 </span> iterator.call(context, val, key, obj) <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
|
||||
<span class="line-numbers"> 64 </span> <span class="Keyword">catch</span> e
|
||||
<span class="line-numbers"> 65 </span> <span class="Keyword">throw</span> e <span class="Keyword">if</span> e <span class="Keyword">isnt</span> breaker
|
||||
<span class="line-numbers"> 66 </span> obj
|
||||
<span class="line-numbers"> 67 </span>
|
||||
<span class="line-numbers"> 68 </span>
|
||||
<span class="line-numbers"> 69 </span> <span class="Comment"><span class="Comment">#</span> Return the results of applying the iterator to each element. Use JavaScript</span>
|
||||
<span class="line-numbers"> 70 </span> <span class="Comment"><span class="Comment">#</span> 1.6's version of map, if possible.</span>
|
||||
<span class="line-numbers"> 71 </span> <span class="FunctionArgument"> _.map: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 72 </span> <span class="Keyword">return</span> obj.map(iterator, context) <span class="Keyword">if</span> (obj <span class="Keyword">and</span> _.isFunction(obj.map))
|
||||
<span class="line-numbers"> 73 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
|
||||
<span class="line-numbers"> 74 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 75 </span> results.push(iterator.call(context, value, index, list))
|
||||
<span class="line-numbers"> 76 </span> results
|
||||
<span class="line-numbers"> 77 </span>
|
||||
<span class="line-numbers"> 78 </span>
|
||||
<span class="line-numbers"> 79 </span> <span class="Comment"><span class="Comment">#</span> Reduce builds up a single result from a list of values. Also known as</span>
|
||||
<span class="line-numbers"> 80 </span> <span class="Comment"><span class="Comment">#</span> inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.</span>
|
||||
<span class="line-numbers"> 81 </span> <span class="FunctionArgument"> _.reduce: obj, memo, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 82 </span> <span class="Keyword">return</span> obj.reduce(_.bind(iterator, context), memo) <span class="Keyword">if</span> (obj <span class="Keyword">and</span> _.isFunction(obj.reduce))
|
||||
<span class="line-numbers"> 83 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 84 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> iterator.call(context, memo, value, index, list)
|
||||
<span class="line-numbers"> 85 </span> memo
|
||||
<span class="line-numbers"> 86 </span>
|
||||
<span class="line-numbers"> 87 </span>
|
||||
<span class="line-numbers"> 88 </span> <span class="Comment"><span class="Comment">#</span> The right-associative version of reduce, also known as foldr. Uses</span>
|
||||
<span class="line-numbers"> 89 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.8's version of reduceRight, if available.</span>
|
||||
<span class="line-numbers"> 90 </span> <span class="FunctionArgument"> _.reduceRight: obj, memo, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 91 </span> <span class="Keyword">return</span> obj.reduceRight(_.bind(iterator, context), memo) <span class="Keyword">if</span> (obj <span class="Keyword">and</span> _.isFunction(obj.reduceRight))
|
||||
<span class="line-numbers"> 92 </span> _.each(_.clone(_.toArray(obj)).reverse())<span class="FunctionArgument"> value, index </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 93 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> iterator.call(context, memo, value, index, obj)
|
||||
<span class="line-numbers"> 94 </span> memo
|
||||
<span class="line-numbers"> 95 </span>
|
||||
<span class="line-numbers"> 96 </span>
|
||||
<span class="line-numbers"> 97 </span> <span class="Comment"><span class="Comment">#</span> Return the first value which passes a truth test.</span>
|
||||
<span class="line-numbers"> 98 </span> <span class="FunctionArgument"> _.detect: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 99 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">null</span>
|
||||
<span class="line-numbers"> 100 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 101 </span> <span class="Keyword">if</span> iterator.call(context, value, index, list)
|
||||
<span class="line-numbers"> 102 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> value
|
||||
<span class="line-numbers"> 103 </span> _.breakLoop()
|
||||
<span class="line-numbers"> 104 </span> result
|
||||
<span class="line-numbers"> 105 </span>
|
||||
<span class="line-numbers"> 106 </span>
|
||||
<span class="line-numbers"> 107 </span> <span class="Comment"><span class="Comment">#</span> Return all the elements that pass a truth test. Use JavaScript 1.6's</span>
|
||||
<span class="line-numbers"> 108 </span> <span class="Comment"><span class="Comment">#</span> filter(), if it exists.</span>
|
||||
<span class="line-numbers"> 109 </span> <span class="FunctionArgument"> _.select: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 110 </span> <span class="Keyword">if</span> obj <span class="Keyword">and</span> _.isFunction(obj.filter) <span class="Keyword">then</span> <span class="Keyword">return</span> obj.filter(iterator, context)
|
||||
<span class="line-numbers"> 111 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
|
||||
<span class="line-numbers"> 112 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 113 </span> results.push(value) <span class="Keyword">if</span> iterator.call(context, value, index, list)
|
||||
<span class="line-numbers"> 114 </span> results
|
||||
<span class="line-numbers"> 115 </span>
|
||||
<span class="line-numbers"> 116 </span>
|
||||
<span class="line-numbers"> 117 </span> <span class="Comment"><span class="Comment">#</span> Return all the elements for which a truth test fails.</span>
|
||||
<span class="line-numbers"> 118 </span> <span class="FunctionArgument"> _.reject: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 119 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> []
|
||||
<span class="line-numbers"> 120 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 121 </span> results.push(value) <span class="Keyword">if</span> <span class="Keyword">not</span> iterator.call(context, value, index, list)
|
||||
<span class="line-numbers"> 122 </span> results
|
||||
<span class="line-numbers"> 123 </span>
|
||||
<span class="line-numbers"> 124 </span>
|
||||
<span class="line-numbers"> 125 </span> <span class="Comment"><span class="Comment">#</span> Determine whether all of the elements match a truth test. Delegate to</span>
|
||||
<span class="line-numbers"> 126 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.6's every(), if it is present.</span>
|
||||
<span class="line-numbers"> 127 </span> <span class="FunctionArgument"> _.all: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 128 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
|
||||
<span class="line-numbers"> 129 </span> <span class="Keyword">return</span> obj.every(iterator, context) <span class="Keyword">if</span> obj <span class="Keyword">and</span> _.isFunction(obj.every)
|
||||
<span class="line-numbers"> 130 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
|
||||
<span class="line-numbers"> 131 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 132 </span> _.breakLoop() <span class="Keyword">unless</span> (<span class="FunctionName">result</span><span class="Keyword">:</span> result <span class="Keyword">and</span> iterator.call(context, value, index, list))
|
||||
<span class="line-numbers"> 133 </span> result
|
||||
<span class="line-numbers"> 134 </span>
|
||||
<span class="line-numbers"> 135 </span>
|
||||
<span class="line-numbers"> 136 </span> <span class="Comment"><span class="Comment">#</span> Determine if at least one element in the object matches a truth test. Use</span>
|
||||
<span class="line-numbers"> 137 </span> <span class="Comment"><span class="Comment">#</span> JavaScript 1.6's some(), if it exists.</span>
|
||||
<span class="line-numbers"> 138 </span> <span class="FunctionArgument"> _.any: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 139 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
|
||||
<span class="line-numbers"> 140 </span> <span class="Keyword">return</span> obj.some(iterator, context) <span class="Keyword">if</span> obj <span class="Keyword">and</span> _.isFunction(obj.some)
|
||||
<span class="line-numbers"> 141 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="BuiltInConstant">false</span>
|
||||
<span class="line-numbers"> 142 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 143 </span> _.breakLoop() <span class="Keyword">if</span> (<span class="FunctionName">result</span><span class="Keyword">:</span> iterator.call(context, value, index, list))
|
||||
<span class="line-numbers"> 144 </span> result
|
||||
<span class="line-numbers"> 145 </span>
|
||||
<span class="line-numbers"> 146 </span>
|
||||
<span class="line-numbers"> 147 </span> <span class="Comment"><span class="Comment">#</span> Determine if a given value is included in the array or object,</span>
|
||||
<span class="line-numbers"> 148 </span> <span class="Comment"><span class="Comment">#</span> based on '==='.</span>
|
||||
<span class="line-numbers"> 149 </span> <span class="FunctionArgument"> _.include: obj, target </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 150 </span> <span class="Keyword">return</span> _.indexOf(obj, target) <span class="Keyword">isnt</span> <span class="Keyword">-</span><span class="Number">1</span> <span class="Keyword">if</span> _.isArray(obj)
|
||||
<span class="line-numbers"> 151 </span> <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
|
||||
<span class="line-numbers"> 152 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> val <span class="Keyword">is</span> target
|
||||
<span class="line-numbers"> 153 </span> <span class="BuiltInConstant">false</span>
|
||||
<span class="line-numbers"> 154 </span>
|
||||
<span class="line-numbers"> 155 </span>
|
||||
<span class="line-numbers"> 156 </span> <span class="Comment"><span class="Comment">#</span> Invoke a method with arguments on every item in a collection.</span>
|
||||
<span class="line-numbers"> 157 </span> <span class="FunctionArgument"> _.invoke: obj, method </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 158 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
|
||||
<span class="line-numbers"> 159 </span> (<span class="Keyword">if</span> method <span class="Keyword">then</span> val[method] <span class="Keyword">else</span> val).apply(val, args) <span class="Keyword">for</span> val <span class="Keyword">in</span> obj
|
||||
<span class="line-numbers"> 160 </span>
|
||||
<span class="line-numbers"> 161 </span>
|
||||
<span class="line-numbers"> 162 </span> <span class="Comment"><span class="Comment">#</span> Convenience version of a common use case of map: fetching a property.</span>
|
||||
<span class="line-numbers"> 163 </span> <span class="FunctionArgument"> _.pluck: obj, key </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 164 </span> _.map(obj, (<span class="FunctionArgument">val </span><span class="Storage">=></span> val[key]))
|
||||
<span class="line-numbers"> 165 </span>
|
||||
<span class="line-numbers"> 166 </span>
|
||||
<span class="line-numbers"> 167 </span> <span class="Comment"><span class="Comment">#</span> Return the maximum item or (item-based computation).</span>
|
||||
<span class="line-numbers"> 168 </span> <span class="FunctionArgument"> _.max: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 169 </span> <span class="Keyword">return</span> Math.max.apply(Math, obj) <span class="Keyword">if</span> <span class="Keyword">not</span> iterator <span class="Keyword">and</span> _.isArray(obj)
|
||||
<span class="line-numbers"> 170 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> {<span class="FunctionName">computed</span><span class="Keyword">:</span> <span class="Keyword">-</span><span class="BuiltInConstant">Infinity</span>}
|
||||
<span class="line-numbers"> 171 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 172 </span> <span class="FunctionName">computed</span><span class="Keyword">:</span> <span class="Keyword">if</span> iterator <span class="Keyword">then</span> iterator.call(context, value, index, list) <span class="Keyword">else</span> value
|
||||
<span class="line-numbers"> 173 </span> computed <span class="Keyword">>=</span> result.computed <span class="Keyword">and</span> (<span class="FunctionName">result</span><span class="Keyword">:</span> {<span class="FunctionName">value</span><span class="Keyword">:</span> value, <span class="FunctionName">computed</span><span class="Keyword">:</span> computed})
|
||||
<span class="line-numbers"> 174 </span> result.value
|
||||
<span class="line-numbers"> 175 </span>
|
||||
<span class="line-numbers"> 176 </span>
|
||||
<span class="line-numbers"> 177 </span> <span class="Comment"><span class="Comment">#</span> Return the minimum element (or element-based computation).</span>
|
||||
<span class="line-numbers"> 178 </span> <span class="FunctionArgument"> _.min: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 179 </span> <span class="Keyword">return</span> Math.min.apply(Math, obj) <span class="Keyword">if</span> <span class="Keyword">not</span> iterator <span class="Keyword">and</span> _.isArray(obj)
|
||||
<span class="line-numbers"> 180 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> {<span class="FunctionName">computed</span><span class="Keyword">:</span> <span class="BuiltInConstant">Infinity</span>}
|
||||
<span class="line-numbers"> 181 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 182 </span> <span class="FunctionName">computed</span><span class="Keyword">:</span> <span class="Keyword">if</span> iterator <span class="Keyword">then</span> iterator.call(context, value, index, list) <span class="Keyword">else</span> value
|
||||
<span class="line-numbers"> 183 </span> computed <span class="Keyword"><</span> result.computed <span class="Keyword">and</span> (<span class="FunctionName">result</span><span class="Keyword">:</span> {<span class="FunctionName">value</span><span class="Keyword">:</span> value, <span class="FunctionName">computed</span><span class="Keyword">:</span> computed})
|
||||
<span class="line-numbers"> 184 </span> result.value
|
||||
<span class="line-numbers"> 185 </span>
|
||||
<span class="line-numbers"> 186 </span>
|
||||
<span class="line-numbers"> 187 </span> <span class="Comment"><span class="Comment">#</span> Sort the object's values by a criteria produced by an iterator.</span>
|
||||
<span class="line-numbers"> 188 </span> <span class="FunctionArgument"> _.sortBy: obj, iterator, context </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 189 </span> _.pluck(((_.map(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 190 </span> {<span class="FunctionName">value</span><span class="Keyword">:</span> value, <span class="FunctionName">criteria</span><span class="Keyword">:</span> iterator.call(context, value, index, list)}
|
||||
<span class="line-numbers"> 191 </span> ).sort()<span class="FunctionArgument"> left, right </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 192 </span> <span class="FunctionName">a</span><span class="Keyword">:</span> left.criteria; <span class="FunctionName">b</span><span class="Keyword">:</span> right.criteria
|
||||
<span class="line-numbers"> 193 </span> <span class="Keyword">if</span> a <span class="Keyword"><</span> b <span class="Keyword">then</span> <span class="Keyword">-</span><span class="Number">1</span> <span class="Keyword">else</span> <span class="Keyword">if</span> a <span class="Keyword">></span> b <span class="Keyword">then</span> <span class="Number">1</span> <span class="Keyword">else</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 194 </span> ), <span class="String"><span class="String">'</span>value<span class="String">'</span></span>)
|
||||
<span class="line-numbers"> 195 </span>
|
||||
<span class="line-numbers"> 196 </span>
|
||||
<span class="line-numbers"> 197 </span> <span class="Comment"><span class="Comment">#</span> Use a comparator function to figure out at what index an object should</span>
|
||||
<span class="line-numbers"> 198 </span> <span class="Comment"><span class="Comment">#</span> be inserted so as to maintain order. Uses binary search.</span>
|
||||
<span class="line-numbers"> 199 </span> <span class="FunctionArgument"> _.sortedIndex: array, obj, iterator </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 200 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
|
||||
<span class="line-numbers"> 201 </span> <span class="FunctionName">low</span><span class="Keyword">:</span> <span class="Number">0</span>; <span class="FunctionName">high</span><span class="Keyword">:</span> array.length
|
||||
<span class="line-numbers"> 202 </span> <span class="Keyword">while</span> low <span class="Keyword"><</span> high
|
||||
<span class="line-numbers"> 203 </span> <span class="FunctionName">mid</span><span class="Keyword">:</span> (low <span class="Keyword">+</span> high) <span class="Keyword">></span><span class="Keyword">></span> <span class="Number">1</span>
|
||||
<span class="line-numbers"> 204 </span> <span class="Keyword">if</span> iterator(array[mid]) <span class="Keyword"><</span> iterator(obj) <span class="Keyword">then</span> <span class="FunctionName">low</span><span class="Keyword">:</span> mid <span class="Keyword">+</span> <span class="Number">1</span> <span class="Keyword">else</span> <span class="FunctionName">high</span><span class="Keyword">:</span> mid
|
||||
<span class="line-numbers"> 205 </span> low
|
||||
<span class="line-numbers"> 206 </span>
|
||||
<span class="line-numbers"> 207 </span>
|
||||
<span class="line-numbers"> 208 </span> <span class="Comment"><span class="Comment">#</span> Convert anything iterable into a real, live array.</span>
|
||||
<span class="line-numbers"> 209 </span> <span class="FunctionArgument"> _.toArray: iterable </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 210 </span> <span class="Keyword">return</span> [] <span class="Keyword">if</span> (<span class="Keyword">!</span>iterable)
|
||||
<span class="line-numbers"> 211 </span> <span class="Keyword">return</span> iterable.toArray() <span class="Keyword">if</span> (iterable.toArray)
|
||||
<span class="line-numbers"> 212 </span> <span class="Keyword">return</span> iterable <span class="Keyword">if</span> (_.isArray(iterable))
|
||||
<span class="line-numbers"> 213 </span> <span class="Keyword">return</span> slice.call(iterable) <span class="Keyword">if</span> (_.isArguments(iterable))
|
||||
<span class="line-numbers"> 214 </span> _.values(iterable)
|
||||
<span class="line-numbers"> 215 </span>
|
||||
<span class="line-numbers"> 216 </span>
|
||||
<span class="line-numbers"> 217 </span> <span class="Comment"><span class="Comment">#</span> Return the number of elements in an object.</span>
|
||||
<span class="line-numbers"> 218 </span> <span class="FunctionArgument"> _.size: obj </span><span class="Storage">=></span> _.toArray(obj).length
|
||||
<span class="line-numbers"> 219 </span>
|
||||
<span class="line-numbers"> 220 </span>
|
||||
<span class="line-numbers"> 221 </span> <span class="Comment"><span class="Comment">#</span> -------------------------- Array Functions: ------------------------------</span>
|
||||
<span class="line-numbers"> 222 </span>
|
||||
<span class="line-numbers"> 223 </span> <span class="Comment"><span class="Comment">#</span> Get the first element of an array. Passing "n" will return the first N</span>
|
||||
<span class="line-numbers"> 224 </span> <span class="Comment"><span class="Comment">#</span> values in the array. Aliased as "head". The "guard" check allows it to work</span>
|
||||
<span class="line-numbers"> 225 </span> <span class="Comment"><span class="Comment">#</span> with _.map.</span>
|
||||
<span class="line-numbers"> 226 </span> <span class="FunctionArgument"> _.first: array, n, guard </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 227 </span> <span class="Keyword">if</span> n <span class="Keyword">and</span> <span class="Keyword">not</span> guard <span class="Keyword">then</span> slice.call(array, <span class="Number">0</span>, n) <span class="Keyword">else</span> array[<span class="Number">0</span>]
|
||||
<span class="line-numbers"> 228 </span>
|
||||
<span class="line-numbers"> 229 </span>
|
||||
<span class="line-numbers"> 230 </span> <span class="Comment"><span class="Comment">#</span> Returns everything but the first entry of the array. Aliased as "tail".</span>
|
||||
<span class="line-numbers"> 231 </span> <span class="Comment"><span class="Comment">#</span> Especially useful on the arguments object. Passing an "index" will return</span>
|
||||
<span class="line-numbers"> 232 </span> <span class="Comment"><span class="Comment">#</span> the rest of the values in the array from that index onward. The "guard"</span>
|
||||
<span class="line-numbers"> 233 </span> <span class="Comment"><span class="Comment">#</span> check allows it to work with _.map.</span>
|
||||
<span class="line-numbers"> 234 </span> <span class="FunctionArgument"> _.rest: array, index, guard </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 235 </span> slice.call(array, <span class="Keyword">if</span> _.isUndefined(index) <span class="Keyword">or</span> guard <span class="Keyword">then</span> <span class="Number">1</span> <span class="Keyword">else</span> index)
|
||||
<span class="line-numbers"> 236 </span>
|
||||
<span class="line-numbers"> 237 </span>
|
||||
<span class="line-numbers"> 238 </span> <span class="Comment"><span class="Comment">#</span> Get the last element of an array.</span>
|
||||
<span class="line-numbers"> 239 </span> <span class="FunctionArgument"> _.last: array </span><span class="Storage">=></span> array[array.length <span class="Keyword">-</span> <span class="Number">1</span>]
|
||||
<span class="line-numbers"> 240 </span>
|
||||
<span class="line-numbers"> 241 </span>
|
||||
<span class="line-numbers"> 242 </span> <span class="Comment"><span class="Comment">#</span> Trim out all falsy values from an array.</span>
|
||||
<span class="line-numbers"> 243 </span> <span class="FunctionArgument"> _.compact: array </span><span class="Storage">=></span> array[i] <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...array.length] <span class="Keyword">when</span> array[i]
|
||||
<span class="line-numbers"> 244 </span>
|
||||
<span class="line-numbers"> 245 </span>
|
||||
<span class="line-numbers"> 246 </span> <span class="Comment"><span class="Comment">#</span> Return a completely flattened version of an array.</span>
|
||||
<span class="line-numbers"> 247 </span> <span class="FunctionArgument"> _.flatten: array </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 248 </span> _.reduce(array, [])<span class="FunctionArgument"> memo, value </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 249 </span> <span class="Keyword">return</span> memo.concat(_.flatten(value)) <span class="Keyword">if</span> _.isArray(value)
|
||||
<span class="line-numbers"> 250 </span> memo.push(value)
|
||||
<span class="line-numbers"> 251 </span> memo
|
||||
<span class="line-numbers"> 252 </span>
|
||||
<span class="line-numbers"> 253 </span>
|
||||
<span class="line-numbers"> 254 </span> <span class="Comment"><span class="Comment">#</span> Return a version of the array that does not contain the specified value(s).</span>
|
||||
<span class="line-numbers"> 255 </span> <span class="FunctionArgument"> _.without: array </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 256 </span> <span class="FunctionName">values</span><span class="Keyword">:</span> _.rest(arguments)
|
||||
<span class="line-numbers"> 257 </span> val <span class="Keyword">for</span> val <span class="Keyword">in</span> _.toArray(array) <span class="Keyword">when</span> <span class="Keyword">not</span> _.include(values, val)
|
||||
<span class="line-numbers"> 258 </span>
|
||||
<span class="line-numbers"> 259 </span>
|
||||
<span class="line-numbers"> 260 </span> <span class="Comment"><span class="Comment">#</span> Produce a duplicate-free version of the array. If the array has already</span>
|
||||
<span class="line-numbers"> 261 </span> <span class="Comment"><span class="Comment">#</span> been sorted, you have the option of using a faster algorithm.</span>
|
||||
<span class="line-numbers"> 262 </span> <span class="FunctionArgument"> _.uniq: array, isSorted </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 263 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> []
|
||||
<span class="line-numbers"> 264 </span> <span class="Keyword">for</span> el, i <span class="Keyword">in</span> _.toArray(array)
|
||||
<span class="line-numbers"> 265 </span> memo.push(el) <span class="Keyword">if</span> i <span class="Keyword">is</span> <span class="Number">0</span> <span class="Keyword">||</span> (<span class="Keyword">if</span> isSorted <span class="Keyword">is</span> <span class="BuiltInConstant">true</span> <span class="Keyword">then</span> _.last(memo) <span class="Keyword">isnt</span> el <span class="Keyword">else</span> <span class="Keyword">not</span> _.include(memo, el))
|
||||
<span class="line-numbers"> 266 </span> memo
|
||||
<span class="line-numbers"> 267 </span>
|
||||
<span class="line-numbers"> 268 </span>
|
||||
<span class="line-numbers"> 269 </span> <span class="Comment"><span class="Comment">#</span> Produce an array that contains every item shared between all the</span>
|
||||
<span class="line-numbers"> 270 </span> <span class="Comment"><span class="Comment">#</span> passed-in arrays.</span>
|
||||
<span class="line-numbers"> 271 </span> <span class="FunctionArgument"> _.intersect: array </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 272 </span> <span class="FunctionName">rest</span><span class="Keyword">:</span> _.rest(arguments)
|
||||
<span class="line-numbers"> 273 </span> _.select(_.uniq(array))<span class="FunctionArgument"> item </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 274 </span> _.all(rest)<span class="FunctionArgument"> other </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 275 </span> _.indexOf(other, item) <span class="Keyword">>=</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 276 </span>
|
||||
<span class="line-numbers"> 277 </span>
|
||||
<span class="line-numbers"> 278 </span> <span class="Comment"><span class="Comment">#</span> Zip together multiple lists into a single array -- elements that share</span>
|
||||
<span class="line-numbers"> 279 </span> <span class="Comment"><span class="Comment">#</span> an index go together.</span>
|
||||
<span class="line-numbers"> 280 </span> <span class="FunctionArgument"> _.zip: </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 281 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.toArray(arguments)
|
||||
<span class="line-numbers"> 282 </span> <span class="FunctionName">length</span><span class="Keyword">:</span> _.max(_.pluck(args, <span class="String"><span class="String">'</span>length<span class="String">'</span></span>))
|
||||
<span class="line-numbers"> 283 </span> <span class="FunctionName">results</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Array</span>(length)
|
||||
<span class="line-numbers"> 284 </span> <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...length]
|
||||
<span class="line-numbers"> 285 </span> results[i]<span class="Keyword">:</span> _.pluck(args, String(i))
|
||||
<span class="line-numbers"> 286 </span> results
|
||||
<span class="line-numbers"> 287 </span>
|
||||
<span class="line-numbers"> 288 </span>
|
||||
<span class="line-numbers"> 289 </span> <span class="Comment"><span class="Comment">#</span> If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),</span>
|
||||
<span class="line-numbers"> 290 </span> <span class="Comment"><span class="Comment">#</span> we need this function. Return the position of the first occurence of an</span>
|
||||
<span class="line-numbers"> 291 </span> <span class="Comment"><span class="Comment">#</span> item in an array, or -1 if the item is not included in the array.</span>
|
||||
<span class="line-numbers"> 292 </span> <span class="FunctionArgument"> _.indexOf: array, item </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 293 </span> <span class="Keyword">return</span> array.indexOf(item) <span class="Keyword">if</span> array.indexOf
|
||||
<span class="line-numbers"> 294 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> <span class="Number">0</span>; <span class="FunctionName">l</span><span class="Keyword">:</span> array.length
|
||||
<span class="line-numbers"> 295 </span> <span class="Keyword">while</span> l <span class="Keyword">-</span> i
|
||||
<span class="line-numbers"> 296 </span> <span class="Keyword">if</span> array[i] <span class="Keyword">is</span> item <span class="Keyword">then</span> <span class="Keyword">return</span> i <span class="Keyword">else</span> i<span class="Keyword">++</span>
|
||||
<span class="line-numbers"> 297 </span> <span class="Keyword">-</span><span class="Number">1</span>
|
||||
<span class="line-numbers"> 298 </span>
|
||||
<span class="line-numbers"> 299 </span>
|
||||
<span class="line-numbers"> 300 </span> <span class="Comment"><span class="Comment">#</span> Provide JavaScript 1.6's lastIndexOf, delegating to the native function,</span>
|
||||
<span class="line-numbers"> 301 </span> <span class="Comment"><span class="Comment">#</span> if possible.</span>
|
||||
<span class="line-numbers"> 302 </span> <span class="FunctionArgument"> _.lastIndexOf: array, item </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 303 </span> <span class="Keyword">return</span> array.lastIndexOf(item) <span class="Keyword">if</span> array.lastIndexOf
|
||||
<span class="line-numbers"> 304 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> array.length
|
||||
<span class="line-numbers"> 305 </span> <span class="Keyword">while</span> i
|
||||
<span class="line-numbers"> 306 </span> <span class="Keyword">if</span> array[i] <span class="Keyword">is</span> item <span class="Keyword">then</span> <span class="Keyword">return</span> i <span class="Keyword">else</span> i<span class="Keyword">--</span>
|
||||
<span class="line-numbers"> 307 </span> <span class="Keyword">-</span><span class="Number">1</span>
|
||||
<span class="line-numbers"> 308 </span>
|
||||
<span class="line-numbers"> 309 </span>
|
||||
<span class="line-numbers"> 310 </span> <span class="Comment"><span class="Comment">#</span> Generate an integer Array containing an arithmetic progression. A port of</span>
|
||||
<span class="line-numbers"> 311 </span> <span class="Comment"><span class="Comment">#</span> the native Python range() function. See:</span>
|
||||
<span class="line-numbers"> 312 </span> <span class="Comment"><span class="Comment">#</span> http://docs.python.org/library/functions.html#range</span>
|
||||
<span class="line-numbers"> 313 </span> <span class="FunctionArgument"> _.range: start, stop, step </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 314 </span> <span class="FunctionName">a</span><span class="Keyword">:</span> _.toArray(arguments)
|
||||
<span class="line-numbers"> 315 </span> <span class="FunctionName">solo</span><span class="Keyword">:</span> a.length <span class="Keyword"><=</span> <span class="Number">1</span>
|
||||
<span class="line-numbers"> 316 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> <span class="FunctionName">start</span><span class="Keyword">:</span> <span class="Keyword">if</span> solo <span class="Keyword">then</span> <span class="Number">0</span> <span class="Keyword">else</span> a[<span class="Number">0</span>];
|
||||
<span class="line-numbers"> 317 </span> <span class="FunctionName">stop</span><span class="Keyword">:</span> <span class="Keyword">if</span> solo <span class="Keyword">then</span> a[<span class="Number">0</span>] <span class="Keyword">else</span> a[<span class="Number">1</span>];
|
||||
<span class="line-numbers"> 318 </span> <span class="FunctionName">step</span><span class="Keyword">:</span> a[<span class="Number">2</span>] <span class="Keyword">or</span> <span class="Number">1</span>
|
||||
<span class="line-numbers"> 319 </span> <span class="FunctionName">len</span><span class="Keyword">:</span> Math.ceil((stop <span class="Keyword">-</span> start) <span class="Keyword">/</span> step)
|
||||
<span class="line-numbers"> 320 </span> <span class="Keyword">return</span> [] <span class="Keyword">if</span> len <span class="Keyword"><=</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 321 </span> <span class="FunctionName">range</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Array</span>(len)
|
||||
<span class="line-numbers"> 322 </span> <span class="FunctionName">idx</span><span class="Keyword">:</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 323 </span> <span class="Keyword">while</span> <span class="BuiltInConstant">true</span>
|
||||
<span class="line-numbers"> 324 </span> <span class="Keyword">return</span> range <span class="Keyword">if</span> (<span class="Keyword">if</span> step <span class="Keyword">></span> <span class="Number">0</span> <span class="Keyword">then</span> i <span class="Keyword">-</span> stop <span class="Keyword">else</span> stop <span class="Keyword">-</span> i) <span class="Keyword">>=</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 325 </span> range[idx]<span class="Keyword">:</span> i
|
||||
<span class="line-numbers"> 326 </span> idx<span class="Keyword">++</span>
|
||||
<span class="line-numbers"> 327 </span> i<span class="Keyword">+</span><span class="Keyword">=</span> step
|
||||
<span class="line-numbers"> 328 </span>
|
||||
<span class="line-numbers"> 329 </span>
|
||||
<span class="line-numbers"> 330 </span> <span class="Comment"><span class="Comment">#</span> ----------------------- Function Functions: -----------------------------</span>
|
||||
<span class="line-numbers"> 331 </span>
|
||||
<span class="line-numbers"> 332 </span> <span class="Comment"><span class="Comment">#</span> Create a function bound to a given object (assigning 'this', and arguments,</span>
|
||||
<span class="line-numbers"> 333 </span> <span class="Comment"><span class="Comment">#</span> optionally). Binding with arguments is also known as 'curry'.</span>
|
||||
<span class="line-numbers"> 334 </span> <span class="FunctionArgument"> _.bind: func, obj </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 335 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
|
||||
<span class="line-numbers"> 336 </span> <span class="FunctionArgument"> </span><span class="Storage">=></span> func.apply(obj <span class="Keyword">or</span> root, args.concat(_.toArray(arguments)))
|
||||
<span class="line-numbers"> 337 </span>
|
||||
<span class="line-numbers"> 338 </span>
|
||||
<span class="line-numbers"> 339 </span> <span class="Comment"><span class="Comment">#</span> Bind all of an object's methods to that object. Useful for ensuring that</span>
|
||||
<span class="line-numbers"> 340 </span> <span class="Comment"><span class="Comment">#</span> all callbacks defined on an object belong to it.</span>
|
||||
<span class="line-numbers"> 341 </span> <span class="FunctionArgument"> _.bindAll: obj </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 342 </span> <span class="FunctionName">funcs</span><span class="Keyword">:</span> <span class="Keyword">if</span> arguments.length <span class="Keyword">></span> <span class="Number">1</span> <span class="Keyword">then</span> _.rest(arguments) <span class="Keyword">else</span> _.functions(obj)
|
||||
<span class="line-numbers"> 343 </span> _.each(funcs, (<span class="FunctionArgument">f </span><span class="Storage">=></span> obj[f]<span class="Keyword">:</span> _.bind(obj[f], obj)))
|
||||
<span class="line-numbers"> 344 </span> obj
|
||||
<span class="line-numbers"> 345 </span>
|
||||
<span class="line-numbers"> 346 </span>
|
||||
<span class="line-numbers"> 347 </span> <span class="Comment"><span class="Comment">#</span> Delays a function for the given number of milliseconds, and then calls</span>
|
||||
<span class="line-numbers"> 348 </span> <span class="Comment"><span class="Comment">#</span> it with the arguments supplied.</span>
|
||||
<span class="line-numbers"> 349 </span> <span class="FunctionArgument"> _.delay: func, wait </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 350 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
|
||||
<span class="line-numbers"> 351 </span> setTimeout((<span class="Storage">=></span> func.apply(func, args)), wait)
|
||||
<span class="line-numbers"> 352 </span>
|
||||
<span class="line-numbers"> 353 </span>
|
||||
<span class="line-numbers"> 354 </span> <span class="Comment"><span class="Comment">#</span> Defers a function, scheduling it to run after the current call stack has</span>
|
||||
<span class="line-numbers"> 355 </span> <span class="Comment"><span class="Comment">#</span> cleared.</span>
|
||||
<span class="line-numbers"> 356 </span> <span class="FunctionArgument"> _.defer: func </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 357 </span> _.delay.apply(_, [func, <span class="Number">1</span>].concat(_.rest(arguments)))
|
||||
<span class="line-numbers"> 358 </span>
|
||||
<span class="line-numbers"> 359 </span>
|
||||
<span class="line-numbers"> 360 </span> <span class="Comment"><span class="Comment">#</span> Returns the first function passed as an argument to the second,</span>
|
||||
<span class="line-numbers"> 361 </span> <span class="Comment"><span class="Comment">#</span> allowing you to adjust arguments, run code before and after, and</span>
|
||||
<span class="line-numbers"> 362 </span> <span class="Comment"><span class="Comment">#</span> conditionally execute the original function.</span>
|
||||
<span class="line-numbers"> 363 </span> <span class="FunctionArgument"> _.wrap: func, wrapper </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 364 </span> <span class="FunctionArgument"> </span><span class="Storage">=></span> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))
|
||||
<span class="line-numbers"> 365 </span>
|
||||
<span class="line-numbers"> 366 </span>
|
||||
<span class="line-numbers"> 367 </span> <span class="Comment"><span class="Comment">#</span> Returns a function that is the composition of a list of functions, each</span>
|
||||
<span class="line-numbers"> 368 </span> <span class="Comment"><span class="Comment">#</span> consuming the return value of the function that follows.</span>
|
||||
<span class="line-numbers"> 369 </span> <span class="FunctionArgument"> _.compose: </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 370 </span> <span class="FunctionName">funcs</span><span class="Keyword">:</span> _.toArray(arguments)
|
||||
<span class="line-numbers"> 371 </span> <span class="FunctionArgument"> </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 372 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.toArray(arguments)
|
||||
<span class="line-numbers"> 373 </span> <span class="Keyword">for</span> i <span class="Keyword">in</span> [(funcs.length <span class="Keyword">-</span> <span class="Number">1</span>)..<span class="Number">0</span>]
|
||||
<span class="line-numbers"> 374 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> [funcs[i].apply(<span class="Variable">this</span>, args)]
|
||||
<span class="line-numbers"> 375 </span> args[<span class="Number">0</span>]
|
||||
<span class="line-numbers"> 376 </span>
|
||||
<span class="line-numbers"> 377 </span>
|
||||
<span class="line-numbers"> 378 </span> <span class="Comment"><span class="Comment">#</span> ------------------------- Object Functions: ----------------------------</span>
|
||||
<span class="line-numbers"> 379 </span>
|
||||
<span class="line-numbers"> 380 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the names of an object's properties.</span>
|
||||
<span class="line-numbers"> 381 </span> <span class="FunctionArgument"> _.keys: obj </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 382 </span> <span class="Keyword">return</span> _.range(<span class="Number">0</span>, obj.length) <span class="Keyword">if</span> _.isArray(obj)
|
||||
<span class="line-numbers"> 383 </span> key <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
|
||||
<span class="line-numbers"> 384 </span>
|
||||
<span class="line-numbers"> 385 </span>
|
||||
<span class="line-numbers"> 386 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the values of an object's properties.</span>
|
||||
<span class="line-numbers"> 387 </span> <span class="FunctionArgument"> _.values: obj </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 388 </span> _.map(obj, _.identity)
|
||||
<span class="line-numbers"> 389 </span>
|
||||
<span class="line-numbers"> 390 </span>
|
||||
<span class="line-numbers"> 391 </span> <span class="Comment"><span class="Comment">#</span> Return a sorted list of the function names available in Underscore.</span>
|
||||
<span class="line-numbers"> 392 </span> <span class="FunctionArgument"> _.functions: obj </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 393 </span> _.select(_.keys(obj)<span class="FunctionArgument">, key </span><span class="Storage">=></span> _.isFunction(obj[key])).sort()
|
||||
<span class="line-numbers"> 394 </span>
|
||||
<span class="line-numbers"> 395 </span>
|
||||
<span class="line-numbers"> 396 </span> <span class="Comment"><span class="Comment">#</span> Extend a given object with all of the properties in a source object.</span>
|
||||
<span class="line-numbers"> 397 </span> <span class="FunctionArgument"> _.extend: destination, source </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 398 </span> <span class="Keyword">for</span> key, val <span class="Keyword">of</span> source
|
||||
<span class="line-numbers"> 399 </span> destination[key]<span class="Keyword">:</span> val
|
||||
<span class="line-numbers"> 400 </span> destination
|
||||
<span class="line-numbers"> 401 </span>
|
||||
<span class="line-numbers"> 402 </span>
|
||||
<span class="line-numbers"> 403 </span> <span class="Comment"><span class="Comment">#</span> Create a (shallow-cloned) duplicate of an object.</span>
|
||||
<span class="line-numbers"> 404 </span> <span class="FunctionArgument"> _.clone: obj </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 405 </span> <span class="Keyword">return</span> obj.slice(<span class="Number">0</span>) <span class="Keyword">if</span> _.isArray(obj)
|
||||
<span class="line-numbers"> 406 </span> _.extend({}, obj)
|
||||
<span class="line-numbers"> 407 </span>
|
||||
<span class="line-numbers"> 408 </span>
|
||||
<span class="line-numbers"> 409 </span> <span class="Comment"><span class="Comment">#</span> Invokes interceptor with the obj, and then returns obj.</span>
|
||||
<span class="line-numbers"> 410 </span> <span class="Comment"><span class="Comment">#</span> The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.</span>
|
||||
<span class="line-numbers"> 411 </span> <span class="FunctionArgument"> _.tap: obj, interceptor </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 412 </span> interceptor(obj)
|
||||
<span class="line-numbers"> 413 </span> obj
|
||||
<span class="line-numbers"> 414 </span>
|
||||
<span class="line-numbers"> 415 </span>
|
||||
<span class="line-numbers"> 416 </span> <span class="Comment"><span class="Comment">#</span> Perform a deep comparison to check if two objects are equal.</span>
|
||||
<span class="line-numbers"> 417 </span> <span class="FunctionArgument"> _.isEqual: a, b </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 418 </span> <span class="Comment"><span class="Comment">#</span> Check object identity.</span>
|
||||
<span class="line-numbers"> 419 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> a <span class="Keyword">is</span> b
|
||||
<span class="line-numbers"> 420 </span> <span class="Comment"><span class="Comment">#</span> Different types?</span>
|
||||
<span class="line-numbers"> 421 </span> <span class="FunctionName">atype</span><span class="Keyword">:</span> <span class="Keyword">typeof</span>(a); <span class="FunctionName">btype</span><span class="Keyword">:</span> <span class="Keyword">typeof</span>(b)
|
||||
<span class="line-numbers"> 422 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> atype <span class="Keyword">isnt</span> btype
|
||||
<span class="line-numbers"> 423 </span> <span class="Comment"><span class="Comment">#</span> Basic equality test (watch out for coercions).</span>
|
||||
<span class="line-numbers"> 424 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> <span class="String"><span class="String">`</span>a == b<span class="String">`</span></span>
|
||||
<span class="line-numbers"> 425 </span> <span class="Comment"><span class="Comment">#</span> One is falsy and the other truthy.</span>
|
||||
<span class="line-numbers"> 426 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> (<span class="Keyword">!</span>a <span class="Keyword">and</span> b) <span class="Keyword">or</span> (a <span class="Keyword">and</span> <span class="Keyword">!</span>b)
|
||||
<span class="line-numbers"> 427 </span> <span class="Comment"><span class="Comment">#</span> One of them implements an isEqual()?</span>
|
||||
<span class="line-numbers"> 428 </span> <span class="Keyword">return</span> a.isEqual(b) <span class="Keyword">if</span> a.isEqual
|
||||
<span class="line-numbers"> 429 </span> <span class="Comment"><span class="Comment">#</span> Check dates' integer values.</span>
|
||||
<span class="line-numbers"> 430 </span> <span class="Keyword">return</span> a.getTime() <span class="Keyword">is</span> b.getTime() <span class="Keyword">if</span> _.isDate(a) <span class="Keyword">and</span> _.isDate(b)
|
||||
<span class="line-numbers"> 431 </span> <span class="Comment"><span class="Comment">#</span> Both are NaN?</span>
|
||||
<span class="line-numbers"> 432 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span> <span class="Keyword">if</span> _.isNaN(a) <span class="Keyword">and</span> _.isNaN(b)
|
||||
<span class="line-numbers"> 433 </span> <span class="Comment"><span class="Comment">#</span> Compare regular expressions.</span>
|
||||
<span class="line-numbers"> 434 </span> <span class="Keyword">if</span> _.isRegExp(a) <span class="Keyword">and</span> _.isRegExp(b)
|
||||
<span class="line-numbers"> 435 </span> <span class="Keyword">return</span> a.source <span class="Keyword">is</span> b.source <span class="Keyword">and</span>
|
||||
<span class="line-numbers"> 436 </span> a.global <span class="Keyword">is</span> b.global <span class="Keyword">and</span>
|
||||
<span class="line-numbers"> 437 </span> a.ignoreCase <span class="Keyword">is</span> b.ignoreCase <span class="Keyword">and</span>
|
||||
<span class="line-numbers"> 438 </span> a.multiline <span class="Keyword">is</span> b.multiline
|
||||
<span class="line-numbers"> 439 </span> <span class="Comment"><span class="Comment">#</span> If a is not an object by this point, we can't handle it.</span>
|
||||
<span class="line-numbers"> 440 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> atype <span class="Keyword">isnt</span> <span class="String"><span class="String">'</span>object<span class="String">'</span></span>
|
||||
<span class="line-numbers"> 441 </span> <span class="Comment"><span class="Comment">#</span> Check for different array lengths before comparing contents.</span>
|
||||
<span class="line-numbers"> 442 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> a.length <span class="Keyword">and</span> (a.length <span class="Keyword">isnt</span> b.length)
|
||||
<span class="line-numbers"> 443 </span> <span class="Comment"><span class="Comment">#</span> Nothing else worked, deep compare the contents.</span>
|
||||
<span class="line-numbers"> 444 </span> <span class="FunctionName">aKeys</span><span class="Keyword">:</span> _.keys(a); <span class="FunctionName">bKeys</span><span class="Keyword">:</span> _.keys(b)
|
||||
<span class="line-numbers"> 445 </span> <span class="Comment"><span class="Comment">#</span> Different object sizes?</span>
|
||||
<span class="line-numbers"> 446 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">false</span> <span class="Keyword">if</span> aKeys.length <span class="Keyword">isnt</span> bKeys.length
|
||||
<span class="line-numbers"> 447 </span> <span class="Comment"><span class="Comment">#</span> Recursive comparison of contents.</span>
|
||||
<span class="line-numbers"> 448 </span> <span class="Comment"><span class="Comment">#</span> for (var key in a) if (!_.isEqual(a[key], b[key])) return false;</span>
|
||||
<span class="line-numbers"> 449 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span>
|
||||
<span class="line-numbers"> 450 </span>
|
||||
<span class="line-numbers"> 451 </span>
|
||||
<span class="line-numbers"> 452 </span> <span class="Comment"><span class="Comment">#</span> Is a given array or object empty?</span>
|
||||
<span class="line-numbers"> 453 </span> <span class="FunctionArgument"> _.isEmpty: obj </span><span class="Storage">=></span> _.keys(obj).length <span class="Keyword">is</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 454 </span>
|
||||
<span class="line-numbers"> 455 </span>
|
||||
<span class="line-numbers"> 456 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a DOM element?</span>
|
||||
<span class="line-numbers"> 457 </span> <span class="FunctionArgument"> _.isElement: obj </span><span class="Storage">=></span> obj <span class="Keyword">and</span> obj.nodeType <span class="Keyword">is</span> <span class="Number">1</span>
|
||||
<span class="line-numbers"> 458 </span>
|
||||
<span class="line-numbers"> 459 </span>
|
||||
<span class="line-numbers"> 460 </span> <span class="Comment"><span class="Comment">#</span> Is a given value an array?</span>
|
||||
<span class="line-numbers"> 461 </span> <span class="FunctionArgument"> _.isArray: obj </span><span class="Storage">=></span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.concat <span class="Keyword">and</span> obj.unshift)
|
||||
<span class="line-numbers"> 462 </span>
|
||||
<span class="line-numbers"> 463 </span>
|
||||
<span class="line-numbers"> 464 </span> <span class="Comment"><span class="Comment">#</span> Is a given variable an arguments object?</span>
|
||||
<span class="line-numbers"> 465 </span> <span class="FunctionArgument"> _.isArguments: obj </span><span class="Storage">=></span> obj <span class="Keyword">and</span> _.isNumber(obj.length) <span class="Keyword">and</span> <span class="Keyword">!</span>_.isArray(obj) <span class="Keyword">and</span> <span class="Keyword">!</span>propertyIsEnumerable.call(obj, <span class="String"><span class="String">'</span>length<span class="String">'</span></span>)
|
||||
<span class="line-numbers"> 466 </span>
|
||||
<span class="line-numbers"> 467 </span>
|
||||
<span class="line-numbers"> 468 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a function?</span>
|
||||
<span class="line-numbers"> 469 </span> <span class="FunctionArgument"> _.isFunction: obj </span><span class="Storage">=></span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.constructor <span class="Keyword">and</span> obj.call <span class="Keyword">and</span> obj.apply)
|
||||
<span class="line-numbers"> 470 </span>
|
||||
<span class="line-numbers"> 471 </span>
|
||||
<span class="line-numbers"> 472 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a string?</span>
|
||||
<span class="line-numbers"> 473 </span> <span class="FunctionArgument"> _.isString: obj </span><span class="Storage">=></span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">is</span> <span class="String"><span class="String">'</span><span class="String">'</span></span> <span class="Keyword">or</span> (obj <span class="Keyword">and</span> obj.charCodeAt <span class="Keyword">and</span> obj.substr))
|
||||
<span class="line-numbers"> 474 </span>
|
||||
<span class="line-numbers"> 475 </span>
|
||||
<span class="line-numbers"> 476 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a number?</span>
|
||||
<span class="line-numbers"> 477 </span> <span class="FunctionArgument"> _.isNumber: obj </span><span class="Storage">=></span> toString.call(obj) <span class="Keyword">is</span> <span class="String"><span class="String">'</span>[object Number]<span class="String">'</span></span>
|
||||
<span class="line-numbers"> 478 </span>
|
||||
<span class="line-numbers"> 479 </span>
|
||||
<span class="line-numbers"> 480 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a Date?</span>
|
||||
<span class="line-numbers"> 481 </span> <span class="FunctionArgument"> _.isDate: obj </span><span class="Storage">=></span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.getTimezoneOffset <span class="Keyword">and</span> obj.setUTCFullYear)
|
||||
<span class="line-numbers"> 482 </span>
|
||||
<span class="line-numbers"> 483 </span>
|
||||
<span class="line-numbers"> 484 </span> <span class="Comment"><span class="Comment">#</span> Is the given value a regular expression?</span>
|
||||
<span class="line-numbers"> 485 </span> <span class="FunctionArgument"> _.isRegExp: obj </span><span class="Storage">=></span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.exec <span class="Keyword">and</span> (obj.ignoreCase <span class="Keyword">or</span> obj.ignoreCase <span class="Keyword">is</span> <span class="BuiltInConstant">false</span>))
|
||||
<span class="line-numbers"> 486 </span>
|
||||
<span class="line-numbers"> 487 </span>
|
||||
<span class="line-numbers"> 488 </span> <span class="Comment"><span class="Comment">#</span> Is the given value NaN -- this one is interesting. NaN != NaN, and</span>
|
||||
<span class="line-numbers"> 489 </span> <span class="Comment"><span class="Comment">#</span> isNaN(undefined) == true, so we make sure it's a number first.</span>
|
||||
<span class="line-numbers"> 490 </span> <span class="FunctionArgument"> _.isNaN: obj </span><span class="Storage">=></span> _.isNumber(obj) <span class="Keyword">and</span> window.isNaN(obj)
|
||||
<span class="line-numbers"> 491 </span>
|
||||
<span class="line-numbers"> 492 </span>
|
||||
<span class="line-numbers"> 493 </span> <span class="Comment"><span class="Comment">#</span> Is a given value equal to null?</span>
|
||||
<span class="line-numbers"> 494 </span> <span class="FunctionArgument"> _.isNull: obj </span><span class="Storage">=></span> obj <span class="Keyword">is</span> <span class="BuiltInConstant">null</span>
|
||||
<span class="line-numbers"> 495 </span>
|
||||
<span class="line-numbers"> 496 </span>
|
||||
<span class="line-numbers"> 497 </span> <span class="Comment"><span class="Comment">#</span> Is a given variable undefined?</span>
|
||||
<span class="line-numbers"> 498 </span> <span class="FunctionArgument"> _.isUndefined: obj </span><span class="Storage">=></span> <span class="Keyword">typeof</span> obj <span class="Keyword">is</span> <span class="String"><span class="String">'</span>undefined<span class="String">'</span></span>
|
||||
<span class="line-numbers"> 499 </span>
|
||||
<span class="line-numbers"> 500 </span>
|
||||
<span class="line-numbers"> 501 </span> <span class="Comment"><span class="Comment">#</span> -------------------------- Utility Functions: --------------------------</span>
|
||||
<span class="line-numbers"> 502 </span>
|
||||
<span class="line-numbers"> 503 </span> <span class="Comment"><span class="Comment">#</span> Run Underscore.js in noConflict mode, returning the '_' variable to its</span>
|
||||
<span class="line-numbers"> 504 </span> <span class="Comment"><span class="Comment">#</span> previous owner. Returns a reference to the Underscore object.</span>
|
||||
<span class="line-numbers"> 505 </span> <span class="FunctionArgument"> _.noConflict: </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 506 </span> <span class="FunctionName">root._</span><span class="Keyword">:</span> previousUnderscore
|
||||
<span class="line-numbers"> 507 </span> <span class="Variable">this</span>
|
||||
<span class="line-numbers"> 508 </span>
|
||||
<span class="line-numbers"> 509 </span>
|
||||
<span class="line-numbers"> 510 </span> <span class="Comment"><span class="Comment">#</span> Keep the identity function around for default iterators.</span>
|
||||
<span class="line-numbers"> 511 </span> <span class="FunctionArgument"> _.identity: value </span><span class="Storage">=></span> value
|
||||
<span class="line-numbers"> 512 </span>
|
||||
<span class="line-numbers"> 513 </span>
|
||||
<span class="line-numbers"> 514 </span> <span class="Comment"><span class="Comment">#</span> Break out of the middle of an iteration.</span>
|
||||
<span class="line-numbers"> 515 </span> <span class="FunctionArgument"> _.breakLoop: </span><span class="Storage">=></span> <span class="Keyword">throw</span> breaker
|
||||
<span class="line-numbers"> 516 </span>
|
||||
<span class="line-numbers"> 517 </span>
|
||||
<span class="line-numbers"> 518 </span> <span class="Comment"><span class="Comment">#</span> Generate a unique integer id (unique within the entire client session).</span>
|
||||
<span class="line-numbers"> 519 </span> <span class="Comment"><span class="Comment">#</span> Useful for temporary DOM ids.</span>
|
||||
<span class="line-numbers"> 520 </span> <span class="FunctionName">idCounter</span><span class="Keyword">:</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 521 </span> <span class="FunctionArgument"> _.uniqueId: prefix </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 522 </span> (prefix <span class="Keyword">or</span> <span class="String"><span class="String">'</span><span class="String">'</span></span>) <span class="Keyword">+</span> idCounter<span class="Keyword">++</span>
|
||||
<span class="line-numbers"> 523 </span>
|
||||
<span class="line-numbers"> 524 </span>
|
||||
<span class="line-numbers"> 525 </span> <span class="Comment"><span class="Comment">#</span> JavaScript templating a-la ERB, pilfered from John Resig's</span>
|
||||
<span class="line-numbers"> 526 </span> <span class="Comment"><span class="Comment">#</span> "Secrets of the JavaScript Ninja", page 83.</span>
|
||||
<span class="line-numbers"> 527 </span> <span class="FunctionArgument"> _.template: str, data </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 528 </span> <span class="String"><span class="String">`</span>var fn = new Function('obj',</span>
|
||||
<span class="line-numbers"> 529 </span> <span class="String"> 'var p=[],print=function(){p.push.apply(p,arguments);};' +</span>
|
||||
<span class="line-numbers"> 530 </span> <span class="String"> 'with(obj){p.push(<span class="UserDefinedConstant">\'</span>' +</span>
|
||||
<span class="line-numbers"> 531 </span> <span class="String"> str.</span>
|
||||
<span class="line-numbers"> 532 </span> <span class="String"> replace(/[<span class="UserDefinedConstant">\r</span><span class="UserDefinedConstant">\t</span><span class="UserDefinedConstant">\n</span>]/g, " ").</span>
|
||||
<span class="line-numbers"> 533 </span> <span class="String"> split("<%").join("<span class="UserDefinedConstant">\t</span>").</span>
|
||||
<span class="line-numbers"> 534 </span> <span class="String"> replace(/((^|%>)[^<span class="UserDefinedConstant">\t</span>]*)'/g, "$1<span class="UserDefinedConstant">\r</span>").</span>
|
||||
<span class="line-numbers"> 535 </span> <span class="String"> replace(/<span class="UserDefinedConstant">\t</span>=(.*?)%>/g, "',$1,'").</span>
|
||||
<span class="line-numbers"> 536 </span> <span class="String"> split("<span class="UserDefinedConstant">\t</span>").join("');").</span>
|
||||
<span class="line-numbers"> 537 </span> <span class="String"> split("%>").join("p.push('").</span>
|
||||
<span class="line-numbers"> 538 </span> <span class="String"> split("<span class="UserDefinedConstant">\r</span>").join("<span class="UserDefinedConstant">\\</span>'") +</span>
|
||||
<span class="line-numbers"> 539 </span> <span class="String"> "');}return p.join('');")<span class="String">`</span></span>
|
||||
<span class="line-numbers"> 540 </span> <span class="Keyword">if</span> data <span class="Keyword">then</span> fn(data) <span class="Keyword">else</span> fn
|
||||
<span class="line-numbers"> 541 </span>
|
||||
<span class="line-numbers"> 542 </span>
|
||||
<span class="line-numbers"> 543 </span> <span class="Comment"><span class="Comment">#</span> ------------------------------- Aliases ----------------------------------</span>
|
||||
<span class="line-numbers"> 544 </span>
|
||||
<span class="line-numbers"> 545 </span> <span class="FunctionName">_.forEach</span><span class="Keyword">:</span> _.each
|
||||
<span class="line-numbers"> 546 </span> <span class="FunctionName">_.foldl</span><span class="Keyword">:</span> <span class="FunctionName">_.inject</span><span class="Keyword">:</span> _.reduce
|
||||
<span class="line-numbers"> 547 </span> <span class="FunctionName">_.foldr</span><span class="Keyword">:</span> _.reduceRight
|
||||
<span class="line-numbers"> 548 </span> <span class="FunctionName">_.filter</span><span class="Keyword">:</span> _.select
|
||||
<span class="line-numbers"> 549 </span> <span class="FunctionName">_.every</span><span class="Keyword">:</span> _.all
|
||||
<span class="line-numbers"> 550 </span> <span class="FunctionName">_.some</span><span class="Keyword">:</span> _.any
|
||||
<span class="line-numbers"> 551 </span> <span class="FunctionName">_.head</span><span class="Keyword">:</span> _.first
|
||||
<span class="line-numbers"> 552 </span> <span class="FunctionName">_.tail</span><span class="Keyword">:</span> _.rest
|
||||
<span class="line-numbers"> 553 </span> <span class="FunctionName">_.methods</span><span class="Keyword">:</span> _.functions
|
||||
<span class="line-numbers"> 554 </span>
|
||||
<span class="line-numbers"> 555 </span>
|
||||
<span class="line-numbers"> 556 </span> <span class="Comment"><span class="Comment">#</span> /*------------------------ Setup the OOP Wrapper: --------------------------*/</span>
|
||||
<span class="line-numbers"> 557 </span>
|
||||
<span class="line-numbers"> 558 </span> <span class="Comment"><span class="Comment">#</span> Helper function to continue chaining intermediate results.</span>
|
||||
<span class="line-numbers"> 559 </span> <span class="FunctionArgument"> result: obj, chain </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 560 </span> <span class="Keyword">if</span> chain <span class="Keyword">then</span> _(obj).chain() <span class="Keyword">else</span> obj
|
||||
<span class="line-numbers"> 561 </span>
|
||||
<span class="line-numbers"> 562 </span>
|
||||
<span class="line-numbers"> 563 </span> <span class="Comment"><span class="Comment">#</span> Add all of the Underscore functions to the wrapper object.</span>
|
||||
<span class="line-numbers"> 564 </span> _.each(_.functions(_))<span class="FunctionArgument"> name </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 565 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> _[name]
|
||||
<span class="line-numbers"> 566 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 567 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.toArray(arguments)
|
||||
<span class="line-numbers"> 568 </span> unshift.call(args, <span class="Variable">this</span>._wrapped)
|
||||
<span class="line-numbers"> 569 </span> result(method.apply(_, args), <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 570 </span>
|
||||
<span class="line-numbers"> 571 </span>
|
||||
<span class="line-numbers"> 572 </span> <span class="Comment"><span class="Comment">#</span> Add all mutator Array functions to the wrapper.</span>
|
||||
<span class="line-numbers"> 573 </span> _.each([<span class="String"><span class="String">'</span>pop<span class="String">'</span></span>, <span class="String"><span class="String">'</span>push<span class="String">'</span></span>, <span class="String"><span class="String">'</span>reverse<span class="String">'</span></span>, <span class="String"><span class="String">'</span>shift<span class="String">'</span></span>, <span class="String"><span class="String">'</span>sort<span class="String">'</span></span>, <span class="String"><span class="String">'</span>splice<span class="String">'</span></span>, <span class="String"><span class="String">'</span>unshift<span class="String">'</span></span>])<span class="FunctionArgument"> name </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 574 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
|
||||
<span class="line-numbers"> 575 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 576 </span> method.apply(<span class="Variable">this</span>._wrapped, arguments)
|
||||
<span class="line-numbers"> 577 </span> result(<span class="Variable">this</span>._wrapped, <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 578 </span>
|
||||
<span class="line-numbers"> 579 </span>
|
||||
<span class="line-numbers"> 580 </span> <span class="Comment"><span class="Comment">#</span> Add all accessor Array functions to the wrapper.</span>
|
||||
<span class="line-numbers"> 581 </span> _.each([<span class="String"><span class="String">'</span>concat<span class="String">'</span></span>, <span class="String"><span class="String">'</span>join<span class="String">'</span></span>, <span class="String"><span class="String">'</span>slice<span class="String">'</span></span>])<span class="FunctionArgument"> name </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 582 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
|
||||
<span class="line-numbers"> 583 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 584 </span> result(method.apply(<span class="Variable">this</span>._wrapped, arguments), <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 585 </span>
|
||||
<span class="line-numbers"> 586 </span>
|
||||
<span class="line-numbers"> 587 </span> <span class="Comment"><span class="Comment">#</span> Start chaining a wrapped Underscore object.</span>
|
||||
<span class="line-numbers"> 588 </span> <span class="FunctionArgument"> wrapper::chain: </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 589 </span> <span class="FunctionName">this._chain</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
|
||||
<span class="line-numbers"> 590 </span> <span class="Variable">this</span>
|
||||
<span class="line-numbers"> 591 </span>
|
||||
<span class="line-numbers"> 592 </span>
|
||||
<span class="line-numbers"> 593 </span> <span class="Comment"><span class="Comment">#</span> Extracts the result from a wrapped and chained object.</span>
|
||||
<span class="line-numbers"> 594 </span> <span class="FunctionArgument"> wrapper::value: </span><span class="Storage">=></span> <span class="Variable">this</span>._wrapped
|
||||
</pre> <p>
|
||||
<a href="http://validator.w3.org/check?uri=referer">
|
||||
<img style="border:0"
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0 Strict" height="31" width="88" />
|
||||
</a>
|
||||
<a href="http://jigsaw.w3.org/css-validator/check?uri=referer">
|
||||
<img style="border:0;width:88px;height:31px"
|
||||
src="http://jigsaw.w3.org/css-validator/images/vcss"
|
||||
alt="Valid CSS!" />
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
173
examples/code.coffee
Normal file
173
examples/code.coffee
Normal file
@@ -0,0 +1,173 @@
|
||||
# Functions:
|
||||
square: x => x * x
|
||||
|
||||
sum: x, y => x + y
|
||||
|
||||
odd: x => x % 2 is 0
|
||||
|
||||
even: x => x % 2 isnt 0
|
||||
|
||||
run_loop: =>
|
||||
fire_events(e => e.stopPropagation())
|
||||
listen()
|
||||
wait()
|
||||
|
||||
# Objects:
|
||||
dense_object_literal: {one: 1, two: 2, three: 3}
|
||||
|
||||
spaced_out_multiline_object: {
|
||||
pi: 3.14159
|
||||
list: [1, 2, 3, 4]
|
||||
regex: /match[ing](every|thing|\/)/gi
|
||||
three: new Idea()
|
||||
|
||||
inner_obj: {
|
||||
freedom: => _.freedom()
|
||||
}
|
||||
}
|
||||
|
||||
# Arrays:
|
||||
stooges: [{moe: 45}, {curly: 43}, {larry: 46}]
|
||||
|
||||
exponents: [(x => x), (x => x * x), (x => x * x * x)]
|
||||
|
||||
empty: []
|
||||
|
||||
multiline: [
|
||||
'line one'
|
||||
'line two'
|
||||
]
|
||||
|
||||
# Conditionals and ternaries.
|
||||
if submarine.shields_up
|
||||
full_speed_ahead()
|
||||
fire_torpedos()
|
||||
else if submarine.sinking
|
||||
abandon_ship()
|
||||
else
|
||||
run_away()
|
||||
|
||||
eldest: if 25 > 21 then liz else marge
|
||||
|
||||
decoration: medal_of_honor if war_hero
|
||||
|
||||
go_to_sleep() unless coffee
|
||||
|
||||
# Returning early:
|
||||
race: =>
|
||||
run()
|
||||
walk()
|
||||
crawl()
|
||||
if tired then return sleep()
|
||||
race()
|
||||
|
||||
# Conditional assignment:
|
||||
good ||= evil
|
||||
wine &&= cheese
|
||||
|
||||
# Nested property access and calls.
|
||||
((moon.turn(360))).shapes[3].move({x: 45, y: 30}).position['top'].offset('x')
|
||||
|
||||
a: b: c: 5
|
||||
|
||||
# Embedded JavaScript.
|
||||
callback(
|
||||
`function(e) { e.stop(); }`
|
||||
)
|
||||
|
||||
# Try/Catch/Finally/Throw.
|
||||
try
|
||||
all_hell_breaks_loose()
|
||||
dogs_and_cats_living_together()
|
||||
throw "up"
|
||||
catch error
|
||||
print(error)
|
||||
finally
|
||||
clean_up()
|
||||
|
||||
try all_hell_breaks_loose() catch error then print(error) finally clean_up()
|
||||
|
||||
# While loops, break and continue.
|
||||
while demand > supply
|
||||
sell()
|
||||
restock()
|
||||
|
||||
while supply > demand then buy()
|
||||
|
||||
while true
|
||||
break if broken
|
||||
continue if continuing
|
||||
|
||||
# Unary operators.
|
||||
!!true
|
||||
|
||||
# Lexical scoping.
|
||||
v_1: 5
|
||||
change_a_and_set_b: =>
|
||||
v_1: 10
|
||||
v_2: 15
|
||||
v_2: 20
|
||||
|
||||
# Array comprehensions.
|
||||
supper: food.capitalize() for food in ['toast', 'cheese', 'wine']
|
||||
|
||||
drink(bottle) for bottle, i in ['soda', 'wine', 'lemonade'] when even(i)
|
||||
|
||||
# Switch statements ("else" serves as a default).
|
||||
activity: switch day
|
||||
when "Tuesday" then eat_breakfast()
|
||||
when "Sunday" then go_to_church()
|
||||
when "Saturday" then go_to_the_park()
|
||||
when "Wednesday"
|
||||
if day is bingo_day
|
||||
go_to_bingo()
|
||||
else
|
||||
eat_breakfast()
|
||||
go_to_work()
|
||||
eat_dinner()
|
||||
else go_to_work()
|
||||
|
||||
# Semicolons can optionally be used instead of newlines.
|
||||
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]
|
||||
|
||||
# Multiline strings with inner quotes.
|
||||
story: "Lorem ipsum dolor \"sit\" amet, consectetuer adipiscing elit,
|
||||
sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
|
||||
aliquam erat volutpat. Ut wisi enim ad."
|
||||
|
||||
# Inheritance and calling super.
|
||||
Animal: =>
|
||||
Animal::move: meters =>
|
||||
alert(this.name + " moved " + meters + "m.")
|
||||
|
||||
Snake: name => this.name: name
|
||||
Snake extends Animal
|
||||
Snake::move: =>
|
||||
alert('Slithering...')
|
||||
super(5)
|
||||
|
||||
Horse: name => this.name: name
|
||||
Horse extends Animal
|
||||
Horse::move: =>
|
||||
alert('Galloping...')
|
||||
super(45)
|
||||
|
||||
sam: new Snake("Sammy the Snake")
|
||||
tom: new Horse("Tommy the Horse")
|
||||
|
||||
sam.move()
|
||||
tom.move()
|
||||
|
||||
# Numbers.
|
||||
a_googol: 1e100
|
||||
hex: 0xff0000
|
||||
negative: -1.0
|
||||
infinity: Infinity
|
||||
nan: NaN
|
||||
|
||||
# Deleting.
|
||||
delete secret.identity
|
||||
@@ -1,7 +1,7 @@
|
||||
# Document Model
|
||||
dc.model.Document: dc.Model.extend({
|
||||
|
||||
constructor: attributes => this.base(attributes).
|
||||
constructor: attributes => this.base(attributes)
|
||||
|
||||
# For display, show either the highlighted search results, or the summary,
|
||||
# if no highlights are available.
|
||||
@@ -9,22 +9,22 @@ dc.model.Document: dc.Model.extend({
|
||||
# version of the summary has all runs of whitespace squeezed out.
|
||||
displaySummary: =>
|
||||
text: this.get('highlight') or this.get('summary') or ''
|
||||
text and text.replace(/\s+/g, ' ').
|
||||
text and text.replace(/\s+/g, ' ')
|
||||
|
||||
# Return a list of the document's metadata. Think about caching this on the
|
||||
# document by binding to Metadata, instead of on-the-fly.
|
||||
metadata: =>
|
||||
docId: this.id
|
||||
_.select(Metadata.models()
|
||||
meta => _.any(meta.get('instances')
|
||||
instance => instance.document_id is docId.).).
|
||||
_.select(Metadata.models(), (meta =>
|
||||
_.any(meta.get('instances'), instance =>
|
||||
instance.document_id is docId)))
|
||||
|
||||
bookmark: pageNumber =>
|
||||
bookmark: new dc.model.Bookmark({title: this.get('title'), page_number: pageNumber, document_id: this.id})
|
||||
Bookmarks.create(bookmark).
|
||||
Bookmarks.create(bookmark)
|
||||
|
||||
# Inspect.
|
||||
toString: => 'Document ' + this.id + ' "' + this.get('title') + '"'.
|
||||
toString: => 'Document ' + this.id + ' "' + this.get('title') + '"'
|
||||
|
||||
})
|
||||
|
||||
@@ -37,31 +37,31 @@ dc.model.DocumentSet: dc.model.RESTfulSet.extend({
|
||||
|
||||
constructor: options =>
|
||||
this.base(options)
|
||||
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText').
|
||||
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText')
|
||||
|
||||
selected: => _.select(this.models(), m => m.get('selected').).
|
||||
selected: => _.select(this.models(), m => m.get('selected'))
|
||||
|
||||
selectedIds: => _.pluck(this.selected(), 'id').
|
||||
selectedIds: => _.pluck(this.selected(), 'id')
|
||||
|
||||
countSelected: => this.selected().length.
|
||||
countSelected: => this.selected().length
|
||||
|
||||
downloadSelectedViewers: =>
|
||||
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip').
|
||||
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip')
|
||||
|
||||
downloadSelectedPDF: =>
|
||||
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('pdf_url')).
|
||||
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip').
|
||||
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('pdf_url'))
|
||||
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip')
|
||||
|
||||
downloadSelectedFullText: =>
|
||||
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('full_text_url')).
|
||||
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip').
|
||||
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('full_text_url'))
|
||||
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip')
|
||||
|
||||
# We override "_onModelEvent" to fire selection changed events when documents
|
||||
# change their selected state.
|
||||
_onModelEvent: e, model =>
|
||||
this.base(e, model)
|
||||
fire: e == dc.Model.CHANGED and model.hasChanged('selected')
|
||||
if fire then _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this))..
|
||||
if fire then _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this))
|
||||
|
||||
})
|
||||
|
||||
153
examples/poignant.coffee
Normal file
153
examples/poignant.coffee
Normal file
@@ -0,0 +1,153 @@
|
||||
# Examples from the Poignant Guide.
|
||||
|
||||
# ['toast', 'cheese', 'wine'].each { |food| print food.capitalize }
|
||||
|
||||
['toast', 'wine', 'cheese'].each(food => print(food.capitalize()))
|
||||
|
||||
|
||||
|
||||
# class LotteryTicket
|
||||
# def picks; @picks; end
|
||||
# def picks=(var); @picks = var; end
|
||||
# def purchased; @purchased; end
|
||||
# def purchased=(var); @purchased = var; end
|
||||
# end
|
||||
|
||||
LotteryTicket: {
|
||||
get_picks: => this.picks
|
||||
set_picks: nums => this.picks: nums
|
||||
get_purchase: => this.purchase
|
||||
set_purchase: amount => this.purchase: amount
|
||||
}
|
||||
|
||||
|
||||
|
||||
# module WishScanner
|
||||
# def scan_for_a_wish
|
||||
# wish = self.read.detect do |thought|
|
||||
# thought.index( 'wish: ' ) == 0
|
||||
# end
|
||||
# wish.gsub( 'wish: ', '' )
|
||||
# end
|
||||
# end
|
||||
|
||||
WishScanner: {
|
||||
scan_for_a_wish: =>
|
||||
wish: this.read().detect(thought => thought.index('wish: ') is 0)
|
||||
wish.replace('wish: ', '')
|
||||
}
|
||||
|
||||
|
||||
|
||||
# class Creature
|
||||
#
|
||||
# # This method applies a hit taken during a fight.
|
||||
# def hit( damage )
|
||||
# p_up = rand( charisma )
|
||||
# if p_up % 9 == 7
|
||||
# @life += p_up / 4
|
||||
# puts "[#{ self.class } magick powers up #{ p_up }!]"
|
||||
# end
|
||||
# @life -= damage
|
||||
# puts "[#{ self.class } has died.]" if @life <= 0
|
||||
# end
|
||||
#
|
||||
# # This method takes one turn in a fight.
|
||||
# def fight( enemy, weapon )
|
||||
# if life <= 0
|
||||
# puts "[#{ self.class } is too dead to fight!]"
|
||||
# return
|
||||
# end
|
||||
#
|
||||
# # Attack the opponent
|
||||
# your_hit = rand( strength + weapon )
|
||||
# puts "[You hit with #{ your_hit } points of damage!]"
|
||||
# enemy.hit( your_hit )
|
||||
#
|
||||
# # Retaliation
|
||||
# p enemy
|
||||
# if enemy.life > 0
|
||||
# enemy_hit = rand( enemy.strength + enemy.weapon )
|
||||
# puts "[Your enemy hit with #{ enemy_hit } points of damage!]"
|
||||
# self.hit( enemy_hit )
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# end
|
||||
|
||||
Creature : {
|
||||
|
||||
# This method applies a hit taken during a fight.
|
||||
hit: damage =>
|
||||
p_up: Math.rand(this.charisma)
|
||||
if p_up % 9 is 7
|
||||
this.life += p_up / 4
|
||||
puts("[" + this.name + " magick powers up " + p_up + "!]")
|
||||
this.life -= damage
|
||||
if this.life <= 0 then puts("[" + this.name + " has died.]")
|
||||
|
||||
# This method takes one turn in a fight.
|
||||
fight: enemy, weapon =>
|
||||
if this.life <= 0 then return puts("[" + this.name + "is too dead to fight!]")
|
||||
|
||||
# Attack the opponent.
|
||||
your_hit: Math.rand(this.strength + weapon)
|
||||
puts("[You hit with " + your_hit + "points of damage!]")
|
||||
enemy.hit(your_hit)
|
||||
|
||||
# Retaliation.
|
||||
puts(enemy)
|
||||
if enemy.life > 0
|
||||
enemy_hit: Math.rand(enemy.strength + enemy.weapon)
|
||||
puts("[Your enemy hit with " + enemy_hit + "points of damage!]")
|
||||
this.hit(enemy_hit)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
# # Get evil idea and swap in code words
|
||||
# print "Enter your new idea: "
|
||||
# idea = gets
|
||||
# code_words.each do |real, code|
|
||||
# idea.gsub!( real, code )
|
||||
# end
|
||||
#
|
||||
# # Save the jibberish to a new file
|
||||
# print "File encoded. Please enter a name for this idea: "
|
||||
# idea_name = gets.strip
|
||||
# File::open( "idea-" + idea_name + ".txt", "w" ) do |f|
|
||||
# f << idea
|
||||
# end
|
||||
|
||||
# Get evil idea and swap in code words
|
||||
print("Enter your new idea: ")
|
||||
idea: gets()
|
||||
code_words.each(real, code => idea.replace(real, code))
|
||||
|
||||
# Save the jibberish to a new file
|
||||
print("File encoded. Please enter a name for this idea: ")
|
||||
idea_name: gets().strip()
|
||||
File.open("idea-" + idea_name + '.txt', 'w', file => file.write(idea))
|
||||
|
||||
|
||||
|
||||
# def wipe_mutterings_from( sentence )
|
||||
# unless sentence.respond_to? :include?
|
||||
# raise ArgumentError,
|
||||
# "cannot wipe mutterings from a #{ sentence.class }"
|
||||
# end
|
||||
# while sentence.include? '('
|
||||
# open = sentence.index( '(' )
|
||||
# close = sentence.index( ')', open )
|
||||
# sentence[open..close] = '' if close
|
||||
# end
|
||||
# end
|
||||
|
||||
wipe_mutterings_from: sentence =>
|
||||
throw new Error("cannot wipe mutterings") unless sentence.indexOf
|
||||
while sentence.indexOf('(') >= 0
|
||||
open: sentence.indexOf('(') - 1
|
||||
close: sentence.indexOf(')') + 1
|
||||
sentence: sentence[0..open] + sentence[close..sentence.length]
|
||||
sentence
|
||||
205
examples/potion.coffee
Normal file
205
examples/potion.coffee
Normal file
@@ -0,0 +1,205 @@
|
||||
# Examples from _why's Potion, the Readme and "Potion: A Short Pamphlet".
|
||||
|
||||
# 5 times: "Odelay!" print.
|
||||
|
||||
print("Odelay!") for i in [1..5]
|
||||
|
||||
|
||||
# add = (x, y): x + y.
|
||||
# add(2, 4) string print
|
||||
|
||||
add: x, y => x + y
|
||||
print(add(2, 4))
|
||||
|
||||
|
||||
# loop: 'quaff' print.
|
||||
|
||||
while true
|
||||
print('quaff')
|
||||
|
||||
|
||||
# ('cheese', 'bread', 'mayo') at (1) print
|
||||
|
||||
print(['cheese', 'bread', 'mayo'][1])
|
||||
|
||||
|
||||
# (language='Potion', pointless=true) at (key='language') print
|
||||
|
||||
print({language: 'Potion', pointless: true}['language'])
|
||||
|
||||
|
||||
# minus = (x, y): x - y.
|
||||
# minus (y=10, x=6)
|
||||
|
||||
minus: x, y => x - y
|
||||
minus(6, 10)
|
||||
|
||||
|
||||
# foods = ('cheese', 'bread', 'mayo')
|
||||
# foods (2)
|
||||
|
||||
foods: ['cheese', 'bread', 'mayo']
|
||||
foods[2]
|
||||
|
||||
|
||||
# (dog='canine', cat='feline', fox='vulpine') each (key, val):
|
||||
# (key, ' is a ', val) join print.
|
||||
|
||||
for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
|
||||
print(key + ' is a ' + val)
|
||||
|
||||
|
||||
# Person = class: /name, /age, /sex.
|
||||
# Person print = ():
|
||||
# ('My name is ', /name, '.') join print.
|
||||
|
||||
Person: =>
|
||||
Person::print: =>
|
||||
print('My name is ' + this.name + '.')
|
||||
|
||||
|
||||
# p = Person ()
|
||||
# p /name string print
|
||||
|
||||
p: new Person()
|
||||
print(p.name)
|
||||
|
||||
|
||||
# Policeman = Person class (rank): /rank = rank.
|
||||
# Policeman print = ():
|
||||
# ('My name is ', /name, ' and I'm a ', /rank, '.') join print.
|
||||
#
|
||||
# Policeman ('Constable') print
|
||||
|
||||
Policeman: rank => this.rank: rank
|
||||
Policeman extends Person
|
||||
Policeman::print: =>
|
||||
print('My name is ' + this.name + " and I'm a " + this.rank + '.')
|
||||
|
||||
print(new Policeman('Constable'))
|
||||
|
||||
|
||||
# app = [window (width=200, height=400)
|
||||
# [para 'Welcome.', button 'OK']]
|
||||
# app first name
|
||||
|
||||
app = {
|
||||
window: {width: 200, height: 200}
|
||||
para: 'Welcome.'
|
||||
button: 'OK'
|
||||
}
|
||||
app.window
|
||||
|
||||
|
||||
# x = 1
|
||||
# y = 2
|
||||
#
|
||||
# x = 1, y = 2
|
||||
|
||||
x: 1
|
||||
y: 2
|
||||
|
||||
x: 1; y: 2
|
||||
|
||||
|
||||
# table = (language='Potion'
|
||||
# pointless=true)
|
||||
|
||||
table: {
|
||||
language: 'Potion'
|
||||
pointless: yes
|
||||
}
|
||||
|
||||
|
||||
# # this foul business...
|
||||
# String length = (): 10.
|
||||
|
||||
# this foul business...
|
||||
String::length: => 10
|
||||
|
||||
|
||||
# block = :
|
||||
# 'potion' print.
|
||||
|
||||
block: =>
|
||||
print('potion')
|
||||
|
||||
|
||||
# if (age > 100): 'ancient'.
|
||||
|
||||
if age > 100 then 'ancient'
|
||||
|
||||
|
||||
# author =
|
||||
# if (title == 'Jonathan Strange & Mr. Norrell'):
|
||||
# 'Susanna Clarke'.
|
||||
# elsif (title == 'The Star Diaries'):
|
||||
# 'Stanislaw Lem'.
|
||||
# elsif (title == 'The Slynx'):
|
||||
# 'Tatyana Tolstaya'.
|
||||
# else:
|
||||
# '... probably Philip K. Dick'.
|
||||
|
||||
switch author
|
||||
when 'Jonathan Strange & Mr. Norrell'
|
||||
'Susanna Clarke'
|
||||
when 'The Star Diaries'
|
||||
'Stanislaw Lem'
|
||||
when 'The Slynx'
|
||||
'Tatyana Tolstaya'
|
||||
else
|
||||
'... probably Philip K. Dick'
|
||||
|
||||
|
||||
# count = 8
|
||||
# while (count > 0):
|
||||
# 'quaff' print
|
||||
# count--.
|
||||
|
||||
count: 8
|
||||
while count > 0
|
||||
print('quaff')
|
||||
count--
|
||||
|
||||
|
||||
# 1 to 5 (a):
|
||||
# a string print.
|
||||
|
||||
print(a) for a in [1..5]
|
||||
|
||||
|
||||
# if (3 ?gender):
|
||||
# "Huh? Numbers are sexed? That's amazing." print.
|
||||
|
||||
if (3).gender?
|
||||
print("Huh? Numbers are sexed? That's amazing.")
|
||||
|
||||
|
||||
# HomePage get = (url):
|
||||
# session = url query ? at ('session').
|
||||
|
||||
HomePage::get: url =>
|
||||
session: url.query.session if url.query?
|
||||
|
||||
|
||||
# BTree = class: /left, /right.
|
||||
# b = BTree ()
|
||||
# b /left = BTree ()
|
||||
# b /right = BTree ()
|
||||
|
||||
BTree: =>
|
||||
b: new BTree()
|
||||
b.left: new BTree()
|
||||
b.right: new BTree()
|
||||
|
||||
|
||||
# BTree = class: /left, /right.
|
||||
# b = BTree ()
|
||||
#
|
||||
# if (b ? /left):
|
||||
# 'left path found!' print.
|
||||
|
||||
BTree: =>
|
||||
b: new BTree()
|
||||
|
||||
print('left path found!') if b.left?
|
||||
20
examples/syntax_errors.coffee
Normal file
20
examples/syntax_errors.coffee
Normal file
@@ -0,0 +1,20 @@
|
||||
# Identifiers run together:
|
||||
# a b c
|
||||
|
||||
# Trailing comma in array:
|
||||
# array: [1, 2, 3, 4, 5,]
|
||||
|
||||
# Unterminated object literal:
|
||||
# obj: { one: 1, two: 2
|
||||
|
||||
# Numbers run together:
|
||||
# 101 202
|
||||
|
||||
# Strings run together:
|
||||
# str: "broken" "words"
|
||||
|
||||
# Forgot to terminate a function:
|
||||
# obj: {
|
||||
# first: a => a[0].
|
||||
# last: a => a[a.length-1]
|
||||
# }
|
||||
594
examples/underscore.coffee
Normal file
594
examples/underscore.coffee
Normal file
@@ -0,0 +1,594 @@
|
||||
|
||||
# Underscore.coffee
|
||||
# (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
# Underscore is freely distributable under the terms of the MIT license.
|
||||
# Portions of Underscore are inspired by or borrowed from Prototype.js,
|
||||
# Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
# For all details and documentation:
|
||||
# http://documentcloud.github.com/underscore/
|
||||
|
||||
|
||||
# ------------------------- Baseline setup ---------------------------------
|
||||
|
||||
# Establish the root object, "window" in the browser, or "global" on the server.
|
||||
root: this
|
||||
|
||||
|
||||
# Save the previous value of the "_" variable.
|
||||
previousUnderscore: root._
|
||||
|
||||
|
||||
# If Underscore is called as a function, it returns a wrapped object that
|
||||
# can be used OO-style. This wrapper holds altered versions of all the
|
||||
# underscore functions. Wrapped objects may be chained.
|
||||
wrapper: obj =>
|
||||
this._wrapped: obj
|
||||
this
|
||||
|
||||
|
||||
# Establish the object that gets thrown to break out of a loop iteration.
|
||||
breaker: if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
|
||||
|
||||
|
||||
# Create a safe reference to the Underscore object forreference below.
|
||||
_: root._: obj => new wrapper(obj)
|
||||
|
||||
|
||||
# Export the Underscore object for CommonJS.
|
||||
if typeof(exports) != 'undefined' then exports._: _
|
||||
|
||||
|
||||
# Create quick reference variables for speed access to core prototypes.
|
||||
slice: Array::slice
|
||||
unshift: Array::unshift
|
||||
toString: Object::toString
|
||||
hasOwnProperty: Object::hasOwnProperty
|
||||
propertyIsEnumerable: Object::propertyIsEnumerable
|
||||
|
||||
|
||||
# Current version.
|
||||
_.VERSION: '0.5.5'
|
||||
|
||||
|
||||
# ------------------------ Collection Functions: ---------------------------
|
||||
|
||||
# The cornerstone, an each implementation.
|
||||
# Handles objects implementing forEach, arrays, and raw objects.
|
||||
_.each: obj, iterator, context =>
|
||||
index: 0
|
||||
try
|
||||
return obj.forEach(iterator, context) if obj.forEach
|
||||
if _.isArray(obj) or _.isArguments(obj)
|
||||
return iterator.call(context, obj[i], i, obj) for i in [0...obj.length]
|
||||
iterator.call(context, val, key, obj) for key, val of obj
|
||||
catch e
|
||||
throw e if e isnt breaker
|
||||
obj
|
||||
|
||||
|
||||
# Return the results of applying the iterator to each element. Use JavaScript
|
||||
# 1.6's version of map, if possible.
|
||||
_.map: obj, iterator, context =>
|
||||
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
|
||||
results: []
|
||||
_.each(obj) value, index, list =>
|
||||
results.push(iterator.call(context, value, index, list))
|
||||
results
|
||||
|
||||
|
||||
# Reduce builds up a single result from a list of values. Also known as
|
||||
# inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
|
||||
_.reduce: obj, memo, iterator, context =>
|
||||
return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
|
||||
_.each(obj) value, index, list =>
|
||||
memo: iterator.call(context, memo, value, index, list)
|
||||
memo
|
||||
|
||||
|
||||
# The right-associative version of reduce, also known as foldr. Uses
|
||||
# JavaScript 1.8's version of reduceRight, if available.
|
||||
_.reduceRight: obj, memo, iterator, context =>
|
||||
return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
|
||||
_.each(_.clone(_.toArray(obj)).reverse()) value, index =>
|
||||
memo: iterator.call(context, memo, value, index, obj)
|
||||
memo
|
||||
|
||||
|
||||
# Return the first value which passes a truth test.
|
||||
_.detect: obj, iterator, context =>
|
||||
result: null
|
||||
_.each(obj) value, index, list =>
|
||||
if iterator.call(context, value, index, list)
|
||||
result: value
|
||||
_.breakLoop()
|
||||
result
|
||||
|
||||
|
||||
# Return all the elements that pass a truth test. Use JavaScript 1.6's
|
||||
# filter(), if it exists.
|
||||
_.select: obj, iterator, context =>
|
||||
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context)
|
||||
results: []
|
||||
_.each(obj) value, index, list =>
|
||||
results.push(value) if iterator.call(context, value, index, list)
|
||||
results
|
||||
|
||||
|
||||
# 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)
|
||||
for key, val of obj
|
||||
return true if val is target
|
||||
false
|
||||
|
||||
|
||||
# Invoke a method with arguments on every item in a collection.
|
||||
_.invoke: obj, method =>
|
||||
args: _.rest(arguments, 2)
|
||||
(if method then val[method] else val).apply(val, args) for val in obj
|
||||
|
||||
|
||||
# Convenience version of a common use case of map: fetching a property.
|
||||
_.pluck: obj, key =>
|
||||
_.map(obj, (val => val[key]))
|
||||
|
||||
|
||||
# Return the maximum item or (item-based computation).
|
||||
_.max: obj, iterator, context =>
|
||||
return Math.max.apply(Math, obj) if not 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: obj, iterator, context =>
|
||||
return Math.min.apply(Math, obj) if not 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
|
||||
|
||||
|
||||
# Sort the object's values by a criteria produced by an iterator.
|
||||
_.sortBy: obj, iterator, context =>
|
||||
_.pluck(((_.map(obj) value, index, list =>
|
||||
{value: value, criteria: iterator.call(context, value, index, list)}
|
||||
).sort() left, right =>
|
||||
a: left.criteria; b: right.criteria
|
||||
if a < b then -1 else if a > b then 1 else 0
|
||||
), 'value')
|
||||
|
||||
|
||||
# Use a comparator function to figure out at what index an object should
|
||||
# be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex: array, obj, iterator =>
|
||||
iterator ||= _.identity
|
||||
low: 0; high: array.length
|
||||
while low < high
|
||||
mid: (low + high) >> 1
|
||||
if iterator(array[mid]) < iterator(obj) then low: mid + 1 else high: mid
|
||||
low
|
||||
|
||||
|
||||
# Convert anything iterable into a real, live array.
|
||||
_.toArray: iterable =>
|
||||
return [] if (!iterable)
|
||||
return iterable.toArray() if (iterable.toArray)
|
||||
return iterable if (_.isArray(iterable))
|
||||
return slice.call(iterable) if (_.isArguments(iterable))
|
||||
_.values(iterable)
|
||||
|
||||
|
||||
# Return the number of elements in an object.
|
||||
_.size: obj => _.toArray(obj).length
|
||||
|
||||
|
||||
# -------------------------- Array Functions: ------------------------------
|
||||
|
||||
# Get the first element of an array. Passing "n" will return the first N
|
||||
# values in the array. Aliased as "head". The "guard" check allows it to work
|
||||
# with _.map.
|
||||
_.first: array, n, guard =>
|
||||
if n and not guard then slice.call(array, 0, n) else array[0]
|
||||
|
||||
|
||||
# Returns everything but the first entry of the array. Aliased as "tail".
|
||||
# Especially useful on the arguments object. Passing an "index" will return
|
||||
# the rest of the values in the array from that index onward. The "guard"
|
||||
# check allows it to work with _.map.
|
||||
_.rest: array, index, guard =>
|
||||
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
|
||||
|
||||
|
||||
# Get the last element of an array.
|
||||
_.last: array => array[array.length - 1]
|
||||
|
||||
|
||||
# Trim out all falsy values from an array.
|
||||
_.compact: array => array[i] for i in [0...array.length] when array[i]
|
||||
|
||||
|
||||
# Return a completely flattened version of an array.
|
||||
_.flatten: array =>
|
||||
_.reduce(array, []) memo, value =>
|
||||
return memo.concat(_.flatten(value)) if _.isArray(value)
|
||||
memo.push(value)
|
||||
memo
|
||||
|
||||
|
||||
# Return a version of the array that does not contain the specified value(s).
|
||||
_.without: array =>
|
||||
values: _.rest(arguments)
|
||||
val for val in _.toArray(array) when not _.include(values, val)
|
||||
|
||||
|
||||
# Produce a duplicate-free version of the array. If the array has already
|
||||
# been sorted, you have the option of using a faster algorithm.
|
||||
_.uniq: array, isSorted =>
|
||||
memo: []
|
||||
for el, i in _.toArray(array)
|
||||
memo.push(el) if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
|
||||
memo
|
||||
|
||||
|
||||
# Produce an array that contains every item shared between all the
|
||||
# passed-in arrays.
|
||||
_.intersect: array =>
|
||||
rest: _.rest(arguments)
|
||||
_.select(_.uniq(array)) item =>
|
||||
_.all(rest) other =>
|
||||
_.indexOf(other, item) >= 0
|
||||
|
||||
|
||||
# Zip together multiple lists into a single array -- elements that share
|
||||
# an index go together.
|
||||
_.zip: =>
|
||||
args: _.toArray(arguments)
|
||||
length: _.max(_.pluck(args, 'length'))
|
||||
results: new Array(length)
|
||||
for i in [0...length]
|
||||
results[i]: _.pluck(args, String(i))
|
||||
results
|
||||
|
||||
|
||||
# If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
|
||||
# we need this function. Return the position of the first occurence of an
|
||||
# item in an array, or -1 if the item is not included in the array.
|
||||
_.indexOf: array, item =>
|
||||
return array.indexOf(item) if array.indexOf
|
||||
i: 0; l: array.length
|
||||
while l - i
|
||||
if array[i] is item then return i else i++
|
||||
-1
|
||||
|
||||
|
||||
# Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
|
||||
# if possible.
|
||||
_.lastIndexOf: array, item =>
|
||||
return array.lastIndexOf(item) if array.lastIndexOf
|
||||
i: array.length
|
||||
while i
|
||||
if array[i] is item then return i else i--
|
||||
-1
|
||||
|
||||
|
||||
# Generate an integer Array containing an arithmetic progression. A port of
|
||||
# the native Python range() function. See:
|
||||
# http://docs.python.org/library/functions.html#range
|
||||
_.range: start, stop, step =>
|
||||
a: _.toArray(arguments)
|
||||
solo: a.length <= 1
|
||||
i: start: if solo then 0 else a[0];
|
||||
stop: if solo then a[0] else a[1];
|
||||
step: a[2] or 1
|
||||
len: Math.ceil((stop - start) / step)
|
||||
return [] if len <= 0
|
||||
range: new Array(len)
|
||||
idx: 0
|
||||
while true
|
||||
return range if (if step > 0 then i - stop else stop - i) >= 0
|
||||
range[idx]: i
|
||||
idx++
|
||||
i+= step
|
||||
|
||||
|
||||
# ----------------------- 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: obj =>
|
||||
funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
|
||||
_.each(funcs, (f => obj[f]: _.bind(obj[f], obj)))
|
||||
obj
|
||||
|
||||
|
||||
# Delays a function for the given number of milliseconds, and then calls
|
||||
# it with the arguments supplied.
|
||||
_.delay: func, wait =>
|
||||
args: _.rest(arguments, 2)
|
||||
setTimeout((=> func.apply(func, args)), wait)
|
||||
|
||||
|
||||
# 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)
|
||||
for i in [(funcs.length - 1)..0]
|
||||
args: [funcs[i].apply(this, args)]
|
||||
args[0]
|
||||
|
||||
|
||||
# ------------------------- Object Functions: ----------------------------
|
||||
|
||||
# Retrieve the names of an object's properties.
|
||||
_.keys: obj =>
|
||||
return _.range(0, obj.length) if _.isArray(obj)
|
||||
key for key, val of obj
|
||||
|
||||
|
||||
# Retrieve the values of an object's properties.
|
||||
_.values: obj =>
|
||||
_.map(obj, _.identity)
|
||||
|
||||
|
||||
# Return a sorted list of the function names available in Underscore.
|
||||
_.functions: obj =>
|
||||
_.select(_.keys(obj), key => _.isFunction(obj[key])).sort()
|
||||
|
||||
|
||||
# Extend a given object with all of the properties in a source object.
|
||||
_.extend: destination, source =>
|
||||
for key, val of source
|
||||
destination[key]: val
|
||||
destination
|
||||
|
||||
|
||||
# Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone: obj =>
|
||||
return obj.slice(0) if _.isArray(obj)
|
||||
_.extend({}, obj)
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# 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 value an array?
|
||||
_.isArray: obj => !!(obj and obj.concat and obj.unshift)
|
||||
|
||||
|
||||
# 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 a function?
|
||||
_.isFunction: obj => !!(obj and obj.constructor and obj.call and obj.apply)
|
||||
|
||||
|
||||
# Is the given value a string?
|
||||
_.isString: obj => !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
|
||||
|
||||
|
||||
# Is a given value a number?
|
||||
_.isNumber: obj => toString.call(obj) is '[object Number]'
|
||||
|
||||
|
||||
# Is a given value a Date?
|
||||
_.isDate: obj => !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
|
||||
|
||||
|
||||
# Is the given value a regular expression?
|
||||
_.isRegExp: obj => !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
|
||||
|
||||
|
||||
# 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 window.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'
|
||||
|
||||
|
||||
# -------------------------- 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.
|
||||
idCounter: 0
|
||||
_.uniqueId: prefix =>
|
||||
(prefix or '') + idCounter++
|
||||
|
||||
|
||||
# JavaScript templating a-la ERB, pilfered from John Resig's
|
||||
# "Secrets of the JavaScript Ninja", page 83.
|
||||
_.template: str, data =>
|
||||
`var fn = new Function('obj',
|
||||
'var p=[],print=function(){p.push.apply(p,arguments);};' +
|
||||
'with(obj){p.push(\'' +
|
||||
str.
|
||||
replace(/[\r\t\n]/g, " ").
|
||||
split("<%").join("\t").
|
||||
replace(/((^|%>)[^\t]*)'/g, "$1\r").
|
||||
replace(/\t=(.*?)%>/g, "',$1,'").
|
||||
split("\t").join("');").
|
||||
split("%>").join("p.push('").
|
||||
split("\r").join("\\'") +
|
||||
"');}return p.join('');")`
|
||||
if data then fn(data) else fn
|
||||
|
||||
|
||||
# ------------------------------- 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.
|
||||
result: obj, chain =>
|
||||
if chain then _(obj).chain() else obj
|
||||
|
||||
|
||||
# Add all of the Underscore functions to the wrapper object.
|
||||
_.each(_.functions(_)) name =>
|
||||
method: _[name]
|
||||
wrapper.prototype[name]: =>
|
||||
args: _.toArray(arguments)
|
||||
unshift.call(args, this._wrapped)
|
||||
result(method.apply(_, args), this._chain)
|
||||
|
||||
|
||||
# Add all mutator Array functions to the wrapper.
|
||||
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift']) name =>
|
||||
method: Array.prototype[name]
|
||||
wrapper.prototype[name]: =>
|
||||
method.apply(this._wrapped, arguments)
|
||||
result(this._wrapped, this._chain)
|
||||
|
||||
|
||||
# Add all accessor Array functions to the wrapper.
|
||||
_.each(['concat', 'join', 'slice']) name =>
|
||||
method: Array.prototype[name]
|
||||
wrapper.prototype[name]: =>
|
||||
result(method.apply(this._wrapped, arguments), this._chain)
|
||||
|
||||
|
||||
# Start chaining a wrapped Underscore object.
|
||||
wrapper::chain: =>
|
||||
this._chain: true
|
||||
this
|
||||
|
||||
|
||||
# Extracts the result from a wrapped and chained object.
|
||||
wrapper::value: => this._wrapped
|
||||
298
grammar.y
298
grammar.y
@@ -1,298 +0,0 @@
|
||||
class Parser
|
||||
|
||||
# Declare tokens produced by the lexer
|
||||
token IF ELSE THEN UNLESS
|
||||
token NUMBER STRING REGEX
|
||||
token TRUE FALSE NULL
|
||||
token IDENTIFIER PROPERTY_ACCESS
|
||||
token CODE PARAM NEW RETURN
|
||||
token TRY CATCH FINALLY THROW
|
||||
token BREAK CONTINUE
|
||||
token FOR IN WHILE
|
||||
token SWITCH CASE DEFAULT
|
||||
token NEWLINE
|
||||
token JS
|
||||
|
||||
# Declare order of operations.
|
||||
prechigh
|
||||
nonassoc UMINUS NOT '!'
|
||||
left '*' '/' '%'
|
||||
left '+' '-'
|
||||
left '<=' '<' '>' '>='
|
||||
right '==' '!=' IS AINT
|
||||
left '&&' '||' AND OR
|
||||
left ':'
|
||||
right '-=' '+=' '/=' '*=' '||=' '&&='
|
||||
nonassoc IF
|
||||
left UNLESS
|
||||
right RETURN THROW FOR WHILE
|
||||
nonassoc "."
|
||||
preclow
|
||||
|
||||
# We expect 2 shift/reduce errors for optional syntax.
|
||||
# There used to be 252 -- greatly improved.
|
||||
expect 2
|
||||
|
||||
rule
|
||||
|
||||
# All parsing will end in this rule, being the trunk of the AST.
|
||||
Root:
|
||||
/* nothing */ { result = Nodes.new([]) }
|
||||
| Terminator { result = Nodes.new([]) }
|
||||
| Expressions { result = val[0] }
|
||||
;
|
||||
|
||||
# Any list of expressions or method body, seperated by line breaks or semis.
|
||||
Expressions:
|
||||
Expression { result = Nodes.new(val) }
|
||||
| Expressions Terminator Expression { result = val[0] << val[2] }
|
||||
| Expressions Terminator { result = val[0] }
|
||||
| Terminator Expressions { result = val[1] }
|
||||
;
|
||||
|
||||
# All types of expressions in our language
|
||||
Expression:
|
||||
Literal
|
||||
| Value
|
||||
| Call
|
||||
| Assign
|
||||
| Code
|
||||
| Operation
|
||||
| If
|
||||
| Try
|
||||
| Throw
|
||||
| Return
|
||||
| While
|
||||
| For
|
||||
| Switch
|
||||
;
|
||||
|
||||
# All tokens that can terminate an expression
|
||||
Terminator:
|
||||
"\n"
|
||||
| ";"
|
||||
;
|
||||
|
||||
# All tokens that can serve to begin the second block
|
||||
Then:
|
||||
THEN
|
||||
| Terminator
|
||||
;
|
||||
|
||||
# All hard-coded values
|
||||
Literal:
|
||||
NUMBER { result = LiteralNode.new(val[0]) }
|
||||
| STRING { result = LiteralNode.new(val[0]) }
|
||||
| JS { result = LiteralNode.new(val[0]) }
|
||||
| REGEX { result = LiteralNode.new(val[0]) }
|
||||
| TRUE { result = LiteralNode.new(true) }
|
||||
| FALSE { result = LiteralNode.new(false) }
|
||||
| NULL { result = LiteralNode.new(nil) }
|
||||
| BREAK { result = LiteralNode.new(val[0]) }
|
||||
| CONTINUE { result = LiteralNode.new(val[0]) }
|
||||
;
|
||||
|
||||
# Assign to a variable
|
||||
Assign:
|
||||
Value ":" Expression { result = AssignNode.new(val[0], val[2]) }
|
||||
;
|
||||
|
||||
# Assignment within an object literal.
|
||||
AssignObj:
|
||||
IDENTIFIER ":" Expression { result = AssignNode.new(val[0], val[2], :object) }
|
||||
;
|
||||
|
||||
# A Return statement.
|
||||
Return:
|
||||
RETURN Expression { result = ReturnNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# Arithmetic and logical operators
|
||||
# For Ruby's Operator precedence, see:
|
||||
# https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
|
||||
Operation:
|
||||
'!' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
|
||||
| NOT Expression { result = OpNode.new(val[0], val[1]) }
|
||||
|
||||
|
||||
| Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '%' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '+' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '-' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '<=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '>=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| 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 '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
;
|
||||
|
||||
|
||||
# Method definition
|
||||
Code:
|
||||
ParamList "=>" Expressions "." { result = CodeNode.new(val[0], val[2]) }
|
||||
| "=>" Expressions "." { result = CodeNode.new([], val[1]) }
|
||||
;
|
||||
|
||||
ParamList:
|
||||
PARAM { result = val }
|
||||
| ParamList "," PARAM { result = val[0] << val[2] }
|
||||
;
|
||||
|
||||
Value:
|
||||
IDENTIFIER { result = ValueNode.new(val) }
|
||||
| Array { result = ValueNode.new(val) }
|
||||
| Object { result = ValueNode.new(val) }
|
||||
| Parenthetical { result = ValueNode.new(val) }
|
||||
| Value Accessor { result = val[0] << val[1] }
|
||||
| Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
|
||||
;
|
||||
|
||||
Accessor:
|
||||
PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
|
||||
| Index { result = val[0] }
|
||||
| Slice { result = val[0] }
|
||||
;
|
||||
|
||||
Index:
|
||||
"[" Expression "]" { result = IndexNode.new(val[1]) }
|
||||
;
|
||||
|
||||
Slice:
|
||||
"[" Expression "," Expression "]" { result = SliceNode.new(val[1], val[3]) }
|
||||
;
|
||||
|
||||
Object:
|
||||
"{" AssignList "}" { result = ObjectNode.new(val[1]) }
|
||||
;
|
||||
|
||||
AssignList:
|
||||
/* nothing */ { result = []}
|
||||
| AssignObj { result = val }
|
||||
| AssignList "," AssignObj { result = val[0] << val[2] }
|
||||
| AssignList Terminator AssignObj { result = val[0] << val[2] }
|
||||
;
|
||||
|
||||
# A method call.
|
||||
Call:
|
||||
Invocation { result = val[0] }
|
||||
| NEW Invocation { result = val[1].new_instance }
|
||||
;
|
||||
|
||||
Invocation:
|
||||
Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
|
||||
;
|
||||
|
||||
# An Array.
|
||||
Array:
|
||||
"[" ArgList "]" { result = ArrayNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# A list of arguments to a method call.
|
||||
ArgList:
|
||||
/* nothing */ { result = [] }
|
||||
| Expression { result = val }
|
||||
| ArgList "," Expression { result = val[0] << val[2] }
|
||||
| ArgList Terminator Expression { result = val[0] << val[2] }
|
||||
;
|
||||
|
||||
If:
|
||||
IF Expression
|
||||
Then Expressions "." { result = IfNode.new(val[1], val[3]) }
|
||||
| IF Expression
|
||||
Then Expressions
|
||||
ELSE Expressions "." { result = IfNode.new(val[1], val[3], val[5]) }
|
||||
| Expression IF Expression { result = IfNode.new(val[2], Nodes.new([val[0]])) }
|
||||
| Expression UNLESS Expression { result = IfNode.new(val[2], Nodes.new([val[0]]), nil, :invert) }
|
||||
;
|
||||
|
||||
Try:
|
||||
TRY Expressions CATCH IDENTIFIER
|
||||
Expressions "." { result = TryNode.new(val[1], val[3], val[4]) }
|
||||
| TRY Expressions FINALLY
|
||||
Expressions "." { result = TryNode.new(val[1], nil, nil, val[3]) }
|
||||
| TRY Expressions CATCH IDENTIFIER
|
||||
Expressions
|
||||
FINALLY Expressions "." { result = TryNode.new(val[1], val[3], val[4], val[6]) }
|
||||
;
|
||||
|
||||
Throw:
|
||||
THROW Expression { result = ThrowNode.new(val[1]) }
|
||||
;
|
||||
|
||||
Parenthetical:
|
||||
"(" Expressions ")" { result = ParentheticalNode.new(val[1]) }
|
||||
;
|
||||
|
||||
While:
|
||||
WHILE Expression Then
|
||||
Expressions "." { result = WhileNode.new(val[1], val[3]) }
|
||||
;
|
||||
|
||||
For:
|
||||
Expression FOR IDENTIFIER
|
||||
IN Expression "." { result = ForNode.new(val[0], val[4], val[2]) }
|
||||
| Expression FOR
|
||||
IDENTIFIER "," IDENTIFIER
|
||||
IN Expression "." { result = ForNode.new(val[0], val[6], val[2], val[4]) }
|
||||
| Expression FOR IDENTIFIER
|
||||
IN Expression
|
||||
IF Expression "." { result = ForNode.new(IfNode.new(val[6], Nodes.new([val[0]])), val[4], val[2]) }
|
||||
| Expression FOR
|
||||
IDENTIFIER "," IDENTIFIER
|
||||
IN Expression
|
||||
IF Expression "." { result = ForNode.new(IfNode.new(val[8], Nodes.new([val[0]])), val[6], val[2], val[4]) }
|
||||
;
|
||||
|
||||
Switch:
|
||||
SWITCH Expression Then
|
||||
Cases "." { result = val[3].rewrite_condition(val[1]) }
|
||||
| SWITCH Expression Then
|
||||
Cases DEFAULT Expressions "." { result = val[3].rewrite_condition(val[1]).add_default(val[5]) }
|
||||
;
|
||||
|
||||
Cases:
|
||||
Case { result = val[0] }
|
||||
| Cases Case { result = val[0] << val[1] }
|
||||
;
|
||||
|
||||
Case:
|
||||
CASE Expression Then Expressions { result = IfNode.new(val[1], val[3]) }
|
||||
;
|
||||
|
||||
end
|
||||
|
||||
---- header
|
||||
require "lexer"
|
||||
require "nodes"
|
||||
|
||||
---- inner
|
||||
def parse(code, show_tokens=false)
|
||||
# @yydebug = true
|
||||
@tokens = Lexer.new.tokenize(code)
|
||||
puts @tokens.inspect if show_tokens
|
||||
do_parse
|
||||
end
|
||||
|
||||
def next_token
|
||||
@tokens.shift
|
||||
end
|
||||
1526
index.html
Normal file
1526
index.html
Normal file
File diff suppressed because it is too large
Load Diff
140
lexer.rb
140
lexer.rb
@@ -1,140 +0,0 @@
|
||||
class Lexer
|
||||
|
||||
KEYWORDS = ["if", "else", "then", "unless",
|
||||
"true", "false", "null",
|
||||
"and", "or", "is", "aint", "not",
|
||||
"new", "return",
|
||||
"try", "catch", "finally", "throw",
|
||||
"break", "continue",
|
||||
"for", "in", "while",
|
||||
"switch", "case", "default"]
|
||||
|
||||
IDENTIFIER = /\A([a-zA-Z$_]\w*)/
|
||||
NUMBER = /\A([0-9]+(\.[0-9]+)?)/
|
||||
STRING = /\A("(.*?)"|'(.*?)')/
|
||||
JS = /\A(`(.*?)`)/
|
||||
OPERATOR = /\A([+\*&|\/\-%=<>]+)/
|
||||
WHITESPACE = /\A([ \t\r]+)/
|
||||
NEWLINE = /\A([\r\n]+)/
|
||||
COMMENT = /\A(#[^\r\n]*)/
|
||||
CODE = /\A(=>)/
|
||||
REGEX = /\A(\/(.*?)\/[imgy]{0,4})/
|
||||
|
||||
JS_CLEANER = /(\A`|`\Z)/
|
||||
|
||||
EXP_START = ['{', '(', '[']
|
||||
EXP_END = ['}', ')', ']']
|
||||
|
||||
# This is how to implement a very simple scanner.
|
||||
# Scan one caracter at the time until you find something to parse.
|
||||
def tokenize(code)
|
||||
@code = code.chomp # Cleanup code by remove extra line breaks
|
||||
@i = 0 # Current character position we're parsing
|
||||
@tokens = [] # Collection of all parsed tokens in the form [:TOKEN_TYPE, value]
|
||||
while @i < @code.length
|
||||
@chunk = @code[@i..-1]
|
||||
extract_next_token
|
||||
end
|
||||
@tokens
|
||||
end
|
||||
|
||||
def extract_next_token
|
||||
return if identifier_token
|
||||
return if number_token
|
||||
return if string_token
|
||||
return if js_token
|
||||
return if regex_token
|
||||
return if remove_comment
|
||||
return if whitespace_token
|
||||
return literal_token
|
||||
end
|
||||
|
||||
# Matching if, print, method names, etc.
|
||||
def identifier_token
|
||||
return false unless identifier = @chunk[IDENTIFIER, 1]
|
||||
# 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
|
||||
if tag == :IDENTIFIER && @tokens[-1] && @tokens[-1][1] == '.'
|
||||
@tokens[-1] = [:PROPERTY_ACCESS, '.']
|
||||
end
|
||||
@tokens << [tag, identifier]
|
||||
@i += identifier.length
|
||||
end
|
||||
|
||||
def number_token
|
||||
return false unless number = @chunk[NUMBER, 1]
|
||||
float = number.include?('.')
|
||||
@tokens << [:NUMBER, float ? number.to_f : number.to_i]
|
||||
@i += number.length
|
||||
end
|
||||
|
||||
def string_token
|
||||
return false unless string = @chunk[STRING, 1]
|
||||
@tokens << [:STRING, string]
|
||||
@i += string.length
|
||||
end
|
||||
|
||||
def js_token
|
||||
return false unless script = @chunk[JS, 1]
|
||||
@tokens << [:JS, script.gsub(JS_CLEANER, '')]
|
||||
@i += script.length
|
||||
end
|
||||
|
||||
def regex_token
|
||||
return false unless regex = @chunk[REGEX, 1]
|
||||
@tokens << [:REGEX, regex]
|
||||
@i += regex.length
|
||||
end
|
||||
|
||||
def remove_comment
|
||||
return false unless comment = @chunk[COMMENT, 1]
|
||||
@i += comment.length
|
||||
end
|
||||
|
||||
# Ignore whitespace
|
||||
def whitespace_token
|
||||
return false unless whitespace = @chunk[WHITESPACE, 1]
|
||||
@i += whitespace.length
|
||||
end
|
||||
|
||||
# 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.
|
||||
def literal_token
|
||||
value = @chunk[NEWLINE, 1]
|
||||
if value
|
||||
@tokens << ["\n", "\n"] unless @tokens.last && @tokens.last[0] == "\n"
|
||||
return @i += value.length
|
||||
end
|
||||
value = @chunk[OPERATOR, 1]
|
||||
tag_parameters if value && value.match(CODE)
|
||||
value ||= @chunk[0,1]
|
||||
skip_following_newlines if EXP_START.include?(value)
|
||||
remove_leading_newlines if EXP_END.include?(value)
|
||||
@tokens << [value, value]
|
||||
@i += value.length
|
||||
end
|
||||
|
||||
# The main source of ambiguity in our grammar was Parameter lists (as opposed
|
||||
# to argument lists in method calls). Tag parameter identifiers to avoid this.
|
||||
def tag_parameters
|
||||
index = 0
|
||||
loop do
|
||||
tok = @tokens[index -= 1]
|
||||
next if tok[0] == ','
|
||||
return if tok[0] != :IDENTIFIER
|
||||
tok[0] = :PARAM
|
||||
end
|
||||
end
|
||||
|
||||
def skip_following_newlines
|
||||
newlines = @code[(@i+1)..-1][NEWLINE, 1]
|
||||
@i += newlines.length if newlines
|
||||
end
|
||||
|
||||
def remove_leading_newlines
|
||||
@tokens.pop if @tokens.last[1] == "\n"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,2 +0,0 @@
|
||||
require "lexer"
|
||||
p Lexer.new.tokenize(File.read('code.cs'))
|
||||
21
lib/coffee-script.rb
Normal file
21
lib/coffee-script.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
||||
require "coffee_script/lexer"
|
||||
require "coffee_script/parser"
|
||||
require "coffee_script/nodes"
|
||||
require "coffee_script/value"
|
||||
require "coffee_script/scope"
|
||||
require "coffee_script/rewriter"
|
||||
require "coffee_script/parse_error"
|
||||
|
||||
# Namespace for all CoffeeScript internal classes.
|
||||
module CoffeeScript
|
||||
|
||||
VERSION = '0.2.4' # Keep in sync with the gemspec.
|
||||
|
||||
# Compile a script (String or IO) to JavaScript.
|
||||
def self.compile(script, options={})
|
||||
script = script.read if script.respond_to?(:read)
|
||||
Parser.new.parse(script).compile(options)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>comments</string>
|
||||
<key>scope</key>
|
||||
<string>source.coffee</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>shellVariables</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>TM_COMMENT_START</string>
|
||||
<key>value</key>
|
||||
<string># </string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>uuid</key>
|
||||
<string>0A92C6F6-4D73-4859-B38C-4CC19CBC191F</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,375 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>comment</key>
|
||||
<string>CoffeeScript Syntax: version 1</string>
|
||||
<key>fileTypes</key>
|
||||
<array>
|
||||
<string>coffee</string>
|
||||
</array>
|
||||
<key>name</key>
|
||||
<string>CoffeeScript</string>
|
||||
<key>foldingStartMarker</key>
|
||||
<string>^.*[:=] \{[^\}]*$</string>
|
||||
<key>foldingStopMarker</key>
|
||||
<string>\s*\}</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.name.function.coffee</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.coffee</string>
|
||||
</dict>
|
||||
<key>3</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.function.coffee</string>
|
||||
</dict>
|
||||
<key>4</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>storage.type.function.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>comment</key>
|
||||
<string>match stuff like: funcName: => … </string>
|
||||
<key>match</key>
|
||||
<string>([a-zA-Z0-9_?.$:*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=>)</string>
|
||||
<key>name</key>
|
||||
<string>meta.function.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.function.coffee</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>storage.type.function.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>comment</key>
|
||||
<string>match stuff like: a => … </string>
|
||||
<key>match</key>
|
||||
<string>([a-zA-Z0-9_?., $:*]*)\s*(=>)</string>
|
||||
<key>name</key>
|
||||
<string>meta.inline.function.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.new.coffee</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.name.type.instance.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>(new)\s+(\w+(?:\.\w*)?)</string>
|
||||
<key>name</key>
|
||||
<string>meta.class.instance.constructor</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<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.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.heredoc.coffee</string>
|
||||
<key>begin</key>
|
||||
<string>("""|''')</string>
|
||||
<key>end</key>
|
||||
<string>("""|''')</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>'</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>'</string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<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.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>"</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>"</string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<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.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>`</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>`</string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<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.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.comment.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>(#).*$\n?</string>
|
||||
<key>name</key>
|
||||
<string>comment.line.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(break|by|catch|continue|else|finally|for|in|of|if|return|switch|then|throw|try|unless|when|while)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b([a-zA-Z$_](\w|\$|:|\.)*)(\:)\s</string>
|
||||
<key>name</key>
|
||||
<string>variable.assignment.coffee</string>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.name.function.coffee</string>
|
||||
</dict>
|
||||
<key>3</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(true|on|yes)\b</string>
|
||||
<key>name</key>
|
||||
<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.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\bnull\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.language.null.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(super|this|extends)\b</string>
|
||||
<key>name</key>
|
||||
<string>variable.language.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(debugger|\\)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.other.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>!|%|&|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(Infinity|NaN|undefined)\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.language.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>(?<=[=(:]|^|return)\s*(/)(?![/*+{}?])</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>(/)[igm]*</string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.regexp.coffee</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escape.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\;</string>
|
||||
<key>name</key>
|
||||
<string>punctuation.terminator.statement.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>,[ |\t]*</string>
|
||||
<key>name</key>
|
||||
<string>meta.delimiter.object.comma.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\.</string>
|
||||
<key>name</key>
|
||||
<string>meta.delimiter.method.period.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\{|\}</string>
|
||||
<key>name</key>
|
||||
<string>meta.brace.curly.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\(|\)</string>
|
||||
<key>name</key>
|
||||
<string>meta.brace.round.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\[|\]</string>
|
||||
<key>name</key>
|
||||
<string>meta.brace.square.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>scopeName</key>
|
||||
<string>source.coffee</string>
|
||||
<key>uuid</key>
|
||||
<string>5B520980-A7D5-4E10-8582-1A4C889A8DE5</string>
|
||||
</dict>
|
||||
</plist>
|
||||
10
lib/coffee_script/CoffeeScript.tmbundle/info.plist
Normal file
10
lib/coffee_script/CoffeeScript.tmbundle/info.plist
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>CoffeeScript</string>
|
||||
<key>uuid</key>
|
||||
<string>A46E4382-F1AC-405B-8F22-65FF470F34D7</string>
|
||||
</dict>
|
||||
</plist>
|
||||
227
lib/coffee_script/command_line.rb
Normal file
227
lib/coffee_script/command_line.rb
Normal file
@@ -0,0 +1,227 @@
|
||||
require 'optparse'
|
||||
require 'fileutils'
|
||||
require 'open3'
|
||||
begin
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../coffee-script')
|
||||
rescue LoadError => e
|
||||
puts(e.message)
|
||||
puts("use \"rake build:parser\" to regenerate parser.rb")
|
||||
exit(1)
|
||||
end
|
||||
|
||||
module CoffeeScript
|
||||
|
||||
# The CommandLine handles all of the functionality of the `coffee`
|
||||
# utility.
|
||||
class CommandLine
|
||||
|
||||
BANNER = <<-EOS
|
||||
coffee compiles CoffeeScript source files into JavaScript.
|
||||
|
||||
Usage:
|
||||
coffee path/to/script.coffee
|
||||
EOS
|
||||
|
||||
# Seconds to pause between checks for changed source files.
|
||||
WATCH_INTERVAL = 0.5
|
||||
|
||||
# Command to execute in Narwhal
|
||||
PACKAGE = File.expand_path(File.dirname(__FILE__) + '/../..')
|
||||
LAUNCHER = "narwhal -p #{PACKAGE} -e 'require(\"coffee-script\").run(system.args);'"
|
||||
|
||||
# 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
|
||||
|
||||
# The "--help" usage message.
|
||||
def usage
|
||||
puts "\n#{@option_parser}\n"
|
||||
exit
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
# Compiles (or partially compiles) the source CoffeeScript file, returning
|
||||
# the desired JS, tokens, or lint results.
|
||||
def compile_javascript(source)
|
||||
script = File.read(source)
|
||||
return tokens(script) if @options[:tokens]
|
||||
js = compile(script, source)
|
||||
return unless js
|
||||
return puts(js) if @options[:print]
|
||||
return lint(js) if @options[:lint]
|
||||
File.open(path_for(source), 'w+') {|f| f.write(js) }
|
||||
end
|
||||
|
||||
# Spins up a watcher thread to keep track of the modification times of the
|
||||
# source files, recompiling them whenever they're saved.
|
||||
def watch_coffee_scripts
|
||||
watch_thread = Thread.start do
|
||||
loop do
|
||||
@sources.each do |source|
|
||||
mtime = File.stat(source).mtime
|
||||
@mtimes[source] ||= mtime
|
||||
if mtime > @mtimes[source]
|
||||
@mtimes[source] = mtime
|
||||
compile_javascript(source)
|
||||
end
|
||||
end
|
||||
sleep WATCH_INTERVAL
|
||||
end
|
||||
end
|
||||
Signal.trap("INT") { watch_thread.kill }
|
||||
watch_thread.join
|
||||
end
|
||||
|
||||
# Ensure that all of the source files exist.
|
||||
def check_sources
|
||||
usage if @sources.empty?
|
||||
missing = @sources.detect {|s| !File.exists?(s) }
|
||||
if missing
|
||||
STDERR.puts("File not found: '#{missing}'")
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
# Pipe compiled JS through JSLint (requires a working 'jsl' command).
|
||||
def lint(js)
|
||||
stdin, stdout, stderr = Open3.popen3('jsl -nologo -stdin')
|
||||
stdin.write(js)
|
||||
stdin.close
|
||||
puts stdout.read.tr("\n", '')
|
||||
errs = stderr.read.chomp
|
||||
puts errs unless errs.empty?
|
||||
stdout.close and stderr.close
|
||||
end
|
||||
|
||||
# Eval a little piece of CoffeeScript directly from the command line.
|
||||
def eval_scriptlet
|
||||
script = STDIN.tty? ? @sources.join(' ') : STDIN.read
|
||||
return tokens(script) if @options[:tokens]
|
||||
js = compile(script)
|
||||
return lint(js) if @options[:lint]
|
||||
puts js
|
||||
end
|
||||
|
||||
# Use Narwhal to run an interactive CoffeeScript session.
|
||||
def launch_repl
|
||||
exec "#{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 "#{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='error')
|
||||
begin
|
||||
options = {}
|
||||
options[:no_wrap] = true if @options[:no_wrap]
|
||||
options[:globals] = true if @options[:globals]
|
||||
CoffeeScript.compile(script, options)
|
||||
rescue CoffeeScript::ParseError, SyntaxError => e
|
||||
STDERR.puts "#{source}: #{e.message}"
|
||||
exit(1) unless @options[:watch]
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Write out JavaScript alongside CoffeeScript unless an output directory
|
||||
# is specified.
|
||||
def path_for(source)
|
||||
filename = File.basename(source, File.extname(source)) + '.js'
|
||||
dir = @options[:output] || File.dirname(source)
|
||||
File.join(dir, filename)
|
||||
end
|
||||
|
||||
# Install the CoffeeScript TextMate bundle to ~/Library.
|
||||
def install_bundle
|
||||
bundle_dir = File.expand_path('~/Library/Application Support/TextMate/Bundles/')
|
||||
FileUtils.cp_r(File.dirname(__FILE__) + '/CoffeeScript.tmbundle', bundle_dir)
|
||||
end
|
||||
|
||||
# Use OptionParser for all the options.
|
||||
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)
|
||||
end
|
||||
opts.on('-w', '--watch', 'watch scripts for changes, and recompile') do |w|
|
||||
@options[:watch] = true
|
||||
end
|
||||
opts.on('-p', '--print', 'print the compiled JavaScript to stdout') do |d|
|
||||
@options[:print] = true
|
||||
end
|
||||
opts.on('-l', '--lint', 'pipe the compiled JavaScript through JSLint') do |l|
|
||||
@options[:lint] = true
|
||||
end
|
||||
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|
|
||||
@options[:tokens] = true
|
||||
end
|
||||
opts.on('-v', '--verbose', 'print at every step of code generation') do |v|
|
||||
ENV['VERBOSE'] = 'true'
|
||||
end
|
||||
opts.on('-n', '--no-wrap', 'raw output, no function safety wrapper') do |n|
|
||||
@options[:no_wrap] = true
|
||||
end
|
||||
opts.on('-g', '--globals', 'attach all top-level variable as globals') do |n|
|
||||
@options[:globals] = true
|
||||
end
|
||||
opts.on_tail('--install-bundle', 'install the CoffeeScript TextMate bundle') do |i|
|
||||
install_bundle
|
||||
exit
|
||||
end
|
||||
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
|
||||
usage
|
||||
end
|
||||
end
|
||||
@option_parser.banner = BANNER
|
||||
begin
|
||||
@option_parser.parse!(ARGV)
|
||||
rescue OptionParser::InvalidOption => e
|
||||
puts e.message
|
||||
exit(1)
|
||||
end
|
||||
@sources = ARGV
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
454
lib/coffee_script/grammar.y
Normal file
454
lib/coffee_script/grammar.y
Normal file
@@ -0,0 +1,454 @@
|
||||
class Parser
|
||||
|
||||
# Declare terminal tokens produced by the lexer.
|
||||
token IF ELSE UNLESS
|
||||
token NUMBER STRING REGEX
|
||||
token TRUE FALSE YES NO ON OFF
|
||||
token IDENTIFIER PROPERTY_ACCESS PROTOTYPE_ACCESS
|
||||
token CODE PARAM NEW RETURN
|
||||
token TRY CATCH FINALLY THROW
|
||||
token BREAK CONTINUE
|
||||
token FOR IN OF BY WHEN WHILE
|
||||
token SWITCH LEADING_WHEN
|
||||
token DELETE INSTANCEOF TYPEOF
|
||||
token SUPER EXTENDS
|
||||
token ARGUMENTS
|
||||
token NEWLINE
|
||||
token COMMENT
|
||||
token JS
|
||||
token INDENT OUTDENT
|
||||
|
||||
# Declare order of operations.
|
||||
prechigh
|
||||
left '?'
|
||||
nonassoc UMINUS NOT '!' '!!' '~' '++' '--'
|
||||
left '*' '/' '%'
|
||||
left '+' '-'
|
||||
left '<<' '>>' '>>>'
|
||||
left '&' '|' '^'
|
||||
left '<=' '<' '>' '>='
|
||||
right '==' '!=' IS ISNT
|
||||
left '&&' '||' AND OR
|
||||
right '-=' '+=' '/=' '*=' '%='
|
||||
right DELETE INSTANCEOF TYPEOF
|
||||
left '.'
|
||||
right INDENT
|
||||
left OUTDENT
|
||||
right WHEN LEADING_WHEN IN OF BY
|
||||
right THROW FOR NEW SUPER
|
||||
left EXTENDS
|
||||
left ASSIGN '||=' '&&='
|
||||
right RETURN
|
||||
right '=>' UNLESS IF ELSE WHILE
|
||||
preclow
|
||||
|
||||
rule
|
||||
|
||||
# All parsing will end in this rule, being the trunk of the AST.
|
||||
Root:
|
||||
/* nothing */ { result = Expressions.new }
|
||||
| Terminator { result = Expressions.new }
|
||||
| Expressions { result = val[0] }
|
||||
| Block Terminator { result = val[0] }
|
||||
;
|
||||
|
||||
# Any list of expressions or method body, seperated by line breaks or semis.
|
||||
Expressions:
|
||||
Expression { result = Expressions.wrap(val) }
|
||||
| Expressions Terminator Expression { result = val[0] << val[2] }
|
||||
| Expressions Terminator { result = val[0] }
|
||||
;
|
||||
|
||||
# All types of expressions in our language. The basic unit of CoffeeScript
|
||||
# is the expression.
|
||||
Expression:
|
||||
Value
|
||||
| Call
|
||||
| Code
|
||||
| Operation
|
||||
| Assign
|
||||
| If
|
||||
| Try
|
||||
| Throw
|
||||
| Return
|
||||
| While
|
||||
| For
|
||||
| Switch
|
||||
| Extends
|
||||
| Splat
|
||||
| Existence
|
||||
| Comment
|
||||
;
|
||||
|
||||
# A block of expressions. Note that the Rewriter will convert some postfix
|
||||
# forms into blocks for us, by altering the token stream.
|
||||
Block:
|
||||
INDENT Expressions OUTDENT { result = val[1] }
|
||||
| INDENT OUTDENT { result = Expressions.new }
|
||||
;
|
||||
|
||||
# Tokens that can terminate an expression.
|
||||
Terminator:
|
||||
"\n"
|
||||
| ";"
|
||||
;
|
||||
|
||||
# All hard-coded values. These can be printed straight to JavaScript.
|
||||
Literal:
|
||||
NUMBER { result = LiteralNode.new(val[0]) }
|
||||
| STRING { result = LiteralNode.new(val[0]) }
|
||||
| JS { result = LiteralNode.new(val[0]) }
|
||||
| REGEX { result = LiteralNode.new(val[0]) }
|
||||
| BREAK { result = LiteralNode.new(val[0]) }
|
||||
| CONTINUE { result = LiteralNode.new(val[0]) }
|
||||
| ARGUMENTS { result = LiteralNode.new(val[0]) }
|
||||
| TRUE { result = LiteralNode.new(true) }
|
||||
| FALSE { result = LiteralNode.new(false) }
|
||||
| YES { result = LiteralNode.new(true) }
|
||||
| NO { result = LiteralNode.new(false) }
|
||||
| ON { result = LiteralNode.new(true) }
|
||||
| OFF { result = LiteralNode.new(false) }
|
||||
;
|
||||
|
||||
# Assignment to a variable (or index).
|
||||
Assign:
|
||||
Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
|
||||
;
|
||||
|
||||
# Assignment within an object literal (can be quoted).
|
||||
AssignObj:
|
||||
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] }
|
||||
;
|
||||
|
||||
# A return statement.
|
||||
Return:
|
||||
RETURN Expression { result = ReturnNode.new(val[1]) }
|
||||
| RETURN { result = ReturnNode.new(ValueNode.new(Value.new('null'))) }
|
||||
;
|
||||
|
||||
# A comment.
|
||||
Comment:
|
||||
COMMENT { result = CommentNode.new(val[0]) }
|
||||
;
|
||||
|
||||
# Arithmetic and logical operators
|
||||
# For Ruby's Operator precedence, see:
|
||||
# https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
|
||||
Operation:
|
||||
'!' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '!!' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '-' 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]) }
|
||||
| DELETE Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| TYPEOF 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]) }
|
||||
| Expression '%' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '+' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '-' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '<<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '>>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '>>>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '|' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '^' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '<=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| 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 IS Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression ISNT Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '%=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression INSTANCEOF Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression IN Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
;
|
||||
|
||||
# The existence operator.
|
||||
Existence:
|
||||
Expression '?' { result = ExistenceNode.new(val[0]) }
|
||||
;
|
||||
|
||||
# Function definition.
|
||||
Code:
|
||||
ParamList "=>" Block { result = CodeNode.new(val[0], val[2]) }
|
||||
| "=>" Block { result = CodeNode.new([], val[1]) }
|
||||
;
|
||||
|
||||
# The parameters to a function definition.
|
||||
ParamList:
|
||||
Param { result = val }
|
||||
| ParamList "," Param { result = val[0] << val[2] }
|
||||
;
|
||||
|
||||
# A Parameter (or ParamSplat) in a function definition.
|
||||
Param:
|
||||
PARAM
|
||||
| PARAM "." "." "." { result = ParamSplatNode.new(val[0]) }
|
||||
;
|
||||
|
||||
# A regular splat.
|
||||
Splat:
|
||||
Expression "." "." "." { result = ArgSplatNode.new(val[0])}
|
||||
;
|
||||
|
||||
# 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]) }
|
||||
| Range { result = ValueNode.new(val[0]) }
|
||||
| Value Accessor { result = val[0] << val[1] }
|
||||
| Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
|
||||
;
|
||||
|
||||
# Accessing into an object or array, through dot or index notation.
|
||||
Accessor:
|
||||
PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
|
||||
| PROTOTYPE_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], true) }
|
||||
| Index { result = val[0] }
|
||||
| Range { result = SliceNode.new(val[0]) }
|
||||
;
|
||||
|
||||
# Indexing into an object or array.
|
||||
Index:
|
||||
"[" Expression "]" { result = IndexNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# An object literal.
|
||||
Object:
|
||||
"{" AssignList "}" { result = ObjectNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# Assignment within an object literal (comma or newline separated).
|
||||
AssignList:
|
||||
/* nothing */ { result = [] }
|
||||
| AssignObj { result = val }
|
||||
| AssignList "," AssignObj { result = val[0] << val[2] }
|
||||
| AssignList Terminator AssignObj { result = val[0] << val[2] }
|
||||
| AssignList ","
|
||||
Terminator AssignObj { result = val[0] << val[3] }
|
||||
| INDENT AssignList OUTDENT { result = val[1] }
|
||||
;
|
||||
|
||||
# All flavors of function call (instantiation, super, and regular).
|
||||
Call:
|
||||
Invocation { result = val[0] }
|
||||
| NEW Invocation { result = val[1].new_instance }
|
||||
| Super { result = val[0] }
|
||||
;
|
||||
|
||||
# Extending an object's prototype.
|
||||
Extends:
|
||||
Value EXTENDS Value { result = ExtendsNode.new(val[0], val[2]) }
|
||||
;
|
||||
|
||||
# A generic function invocation.
|
||||
Invocation:
|
||||
Value Arguments { result = CallNode.new(val[0], val[1]) }
|
||||
| Invocation Arguments { result = CallNode.new(val[0], val[1]) }
|
||||
# | Invocation Code { result = val[0] << val[1] }
|
||||
;
|
||||
|
||||
# The list of arguments to a function invocation.
|
||||
Arguments:
|
||||
"(" ArgList ")" { result = val[1] }
|
||||
| "(" ArgList ")" Code { result = val[1] << val[3] }
|
||||
;
|
||||
|
||||
# Calling super.
|
||||
Super:
|
||||
SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
|
||||
;
|
||||
|
||||
# The range literal.
|
||||
Range:
|
||||
"[" Expression
|
||||
"." "." Expression "]" { result = RangeNode.new(val[1], val[4]) }
|
||||
| "[" Expression
|
||||
"." "." "." Expression "]" { result = RangeNode.new(val[1], val[5], true) }
|
||||
;
|
||||
|
||||
# The array literal.
|
||||
Array:
|
||||
"[" ArgList "]" { result = ArrayNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# A list of arguments to a method call, or as the contents of an array.
|
||||
ArgList:
|
||||
/* nothing */ { result = [] }
|
||||
| Expression { result = val }
|
||||
| INDENT Expression { result = [val[1]] }
|
||||
| ArgList "," Expression { result = val[0] << val[2] }
|
||||
| ArgList Terminator Expression { result = val[0] << val[2] }
|
||||
| ArgList "," Terminator Expression { result = val[0] << val[3] }
|
||||
| ArgList "," INDENT Expression { result = val[0] << val[3] }
|
||||
| ArgList OUTDENT { result = val[0] }
|
||||
;
|
||||
|
||||
# Try/catch/finally exception handling blocks.
|
||||
Try:
|
||||
TRY Block Catch { result = TryNode.new(val[1], val[2][0], val[2][1]) }
|
||||
| TRY Block FINALLY Block { result = TryNode.new(val[1], nil, nil, val[3]) }
|
||||
| TRY Block Catch
|
||||
FINALLY Block { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) }
|
||||
;
|
||||
|
||||
# A catch clause.
|
||||
Catch:
|
||||
CATCH IDENTIFIER Block { result = [val[1], val[2]] }
|
||||
;
|
||||
|
||||
# Throw an exception.
|
||||
Throw:
|
||||
THROW Expression { result = ThrowNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# Parenthetical expressions.
|
||||
Parenthetical:
|
||||
"(" Expression ")" { result = ParentheticalNode.new(val[1], val[0].line) }
|
||||
;
|
||||
|
||||
# The while loop. (there is no do..while).
|
||||
While:
|
||||
WHILE Expression Block { result = WhileNode.new(val[1], val[2]) }
|
||||
| WHILE Expression { result = WhileNode.new(val[1], nil) }
|
||||
;
|
||||
|
||||
# Array comprehensions, including guard and current index.
|
||||
# Looks a little confusing, check nodes.rb for the arguments to ForNode.
|
||||
For:
|
||||
Expression FOR
|
||||
ForVariables ForSource { result = ForNode.new(val[0], val[3], val[2][0], val[2][1]) }
|
||||
| FOR ForVariables ForSource Block { result = ForNode.new(val[3], val[2], val[1][0], val[1][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 Expression { result = {:source => val[1]} }
|
||||
| OF Expression { result = {:source => val[1], :object => true} }
|
||||
| ForSource
|
||||
WHEN Expression { result = val[0].merge(:filter => val[2]) }
|
||||
| ForSource
|
||||
BY Expression { result = val[0].merge(:step => val[2]) }
|
||||
;
|
||||
|
||||
# Switch/When blocks.
|
||||
Switch:
|
||||
SWITCH Expression INDENT
|
||||
Whens OUTDENT { result = val[3].rewrite_condition(val[1]) }
|
||||
| SWITCH Expression INDENT
|
||||
Whens ELSE Block OUTDENT { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
|
||||
;
|
||||
|
||||
# The inner list of whens.
|
||||
Whens:
|
||||
When { result = val[0] }
|
||||
| Whens When { result = val[0] << val[1] }
|
||||
;
|
||||
|
||||
# An individual when.
|
||||
When:
|
||||
LEADING_WHEN Expression Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
|
||||
| LEADING_WHEN Expression Block
|
||||
Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
|
||||
| Comment Terminator When { result = val[2].add_comment(val[0]) }
|
||||
;
|
||||
|
||||
# The most basic form of "if".
|
||||
IfBlock:
|
||||
IF Expression Block { result = IfNode.new(val[1], val[2]) }
|
||||
;
|
||||
|
||||
# An elsif portion of an if-else block.
|
||||
ElsIf:
|
||||
ELSE IfBlock { result = val[1].force_statement }
|
||||
;
|
||||
|
||||
# Multiple elsifs can be chained together.
|
||||
ElsIfs:
|
||||
ElsIf { result = val[0] }
|
||||
| ElsIfs ElsIf { result = val[0].add_else(val[1]) }
|
||||
;
|
||||
|
||||
# Terminating else bodies are strictly optional.
|
||||
ElseBody
|
||||
/* nothing */ { result = nil }
|
||||
| ELSE Block { result = val[1] }
|
||||
;
|
||||
|
||||
# All the alternatives for ending an if-else block.
|
||||
IfEnd:
|
||||
ElseBody { result = val[0] }
|
||||
| ElsIfs ElseBody { result = val[0].add_else(val[1]) }
|
||||
;
|
||||
|
||||
# The full complement of if blocks, including postfix one-liner ifs and unlesses.
|
||||
If:
|
||||
IfBlock IfEnd { result = val[0].add_else(val[1]) }
|
||||
| Expression IF Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true}) }
|
||||
| Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true, :invert => true}) }
|
||||
;
|
||||
|
||||
end
|
||||
|
||||
---- header
|
||||
module CoffeeScript
|
||||
|
||||
---- inner
|
||||
# Lex and parse a CoffeeScript.
|
||||
def parse(code)
|
||||
# Uncomment the following line to enable grammar debugging, in combination
|
||||
# with the -g flag in the Rake build task.
|
||||
# @yydebug = true
|
||||
@tokens = Lexer.new.tokenize(code)
|
||||
do_parse
|
||||
end
|
||||
|
||||
# Retrieve the next token from the list.
|
||||
def next_token
|
||||
@tokens.shift
|
||||
end
|
||||
|
||||
# Raise a custom error class that knows about line numbers.
|
||||
def on_error(error_token_id, error_value, value_stack)
|
||||
raise ParseError.new(token_to_str(error_token_id), error_value, value_stack)
|
||||
end
|
||||
|
||||
---- footer
|
||||
end
|
||||
252
lib/coffee_script/lexer.rb
Normal file
252
lib/coffee_script/lexer.rb
Normal file
@@ -0,0 +1,252 @@
|
||||
module CoffeeScript
|
||||
|
||||
# The lexer reads a stream of CoffeeScript and divvys it up into tagged
|
||||
# tokens. A minor bit of the ambiguity in the grammar has been avoided by
|
||||
# pushing some extra smarts into the Lexer.
|
||||
class Lexer
|
||||
|
||||
# The list of keywords passed verbatim to the parser.
|
||||
KEYWORDS = ["if", "else", "then", "unless",
|
||||
"true", "false", "yes", "no", "on", "off",
|
||||
"and", "or", "is", "isnt", "not",
|
||||
"new", "return",
|
||||
"try", "catch", "finally", "throw",
|
||||
"break", "continue",
|
||||
"for", "in", "of", "by", "where", "while",
|
||||
"switch", "when",
|
||||
"super", "extends",
|
||||
"arguments",
|
||||
"delete", "instanceof", "typeof"]
|
||||
|
||||
# Token matching regexes.
|
||||
IDENTIFIER = /\A([a-zA-Z$_](\w|\$)*)/
|
||||
NUMBER = /\A(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
|
||||
STRING = /\A(""|''|"(.*?)([^\\]|\\\\)"|'(.*?)([^\\]|\\\\)')/m
|
||||
HEREDOC = /\A("{6}|'{6}|"{3}\n?(\s*)(.*?)\n?(\s*)"{3}|'{3}\n?(\s*)(.*?)\n?(\s*)'{3})/m
|
||||
JS = /\A(``|`(.*?)([^\\]|\\\\)`)/m
|
||||
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
|
||||
WHITESPACE = /\A([ \t]+)/
|
||||
COMMENT = /\A(((\n?[ \t]*)?#.*$)+)/
|
||||
CODE = /\A(=>)/
|
||||
REGEX = /\A(\/(.*?)([^\\]|\\\\)\/[imgy]{0,4})/
|
||||
MULTI_DENT = /\A((\n([ \t]*))+)(\.)?/
|
||||
LAST_DENT = /\n([ \t]*)/
|
||||
ASSIGNMENT = /\A(:|=)\Z/
|
||||
|
||||
# Token cleaning regexes.
|
||||
JS_CLEANER = /(\A`|`\Z)/
|
||||
MULTILINER = /\n/
|
||||
COMMENT_CLEANER = /(^\s*#|\n\s*$)/
|
||||
NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/
|
||||
|
||||
# Tokens which a regular expression will never immediately follow, but which
|
||||
# a division operator might.
|
||||
# See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
|
||||
NOT_REGEX = [
|
||||
:IDENTIFIER, :NUMBER, :REGEX, :STRING,
|
||||
')', '++', '--', ']', '}',
|
||||
:FALSE, :NULL, :THIS, :TRUE
|
||||
]
|
||||
|
||||
# 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
|
||||
@i = 0 # Current character position we're parsing
|
||||
@line = 1 # The current line.
|
||||
@indent = 0 # The current indent level.
|
||||
@indents = [] # The stack of all indent levels we are currently within.
|
||||
@tokens = [] # Collection of all parsed tokens in the form [:TOKEN_TYPE, value]
|
||||
while @i < @code.length
|
||||
@chunk = @code[@i..-1]
|
||||
extract_next_token
|
||||
end
|
||||
puts "original stream: #{@tokens.inspect}" if ENV['VERBOSE']
|
||||
close_indentation
|
||||
Rewriter.new.rewrite(@tokens)
|
||||
end
|
||||
|
||||
# At every position, run through this list of attempted matches,
|
||||
# short-circuiting if any of them succeed.
|
||||
def extract_next_token
|
||||
return if identifier_token
|
||||
return if number_token
|
||||
return if heredoc_token
|
||||
return if string_token
|
||||
return if js_token
|
||||
return if regex_token
|
||||
return if indent_token
|
||||
return if comment_token
|
||||
return if whitespace_token
|
||||
return literal_token
|
||||
end
|
||||
|
||||
# Tokenizers ==========================================================
|
||||
|
||||
# Matches identifying literals: variables, keywords, method names, etc.
|
||||
def identifier_token
|
||||
return false unless identifier = @chunk[IDENTIFIER, 1]
|
||||
# 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
|
||||
tag = :LEADING_WHEN if tag == :WHEN && [:OUTDENT, :INDENT, "\n"].include?(last_tag)
|
||||
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2] && @tokens[-2][1] == '.')
|
||||
@tokens[-1][0] = :PROTOTYPE_ACCESS if tag == :IDENTIFIER && last_value == '::'
|
||||
token(tag, identifier)
|
||||
@i += identifier.length
|
||||
end
|
||||
|
||||
# Matches numbers, including decimals, hex, and exponential notation.
|
||||
def number_token
|
||||
return false unless number = @chunk[NUMBER, 1]
|
||||
token(:NUMBER, number)
|
||||
@i += number.length
|
||||
end
|
||||
|
||||
# Matches strings, including multi-line strings.
|
||||
def string_token
|
||||
return false unless string = @chunk[STRING, 1]
|
||||
escaped = string.gsub(MULTILINER, " \\\n")
|
||||
token(:STRING, escaped)
|
||||
@line += string.count("\n")
|
||||
@i += string.length
|
||||
end
|
||||
|
||||
# Matches heredocs, adjusting indentation to the correct level.
|
||||
def heredoc_token
|
||||
return false unless match = @chunk.match(HEREDOC)
|
||||
indent = match[2] || match[5]
|
||||
doc = match[3] || match[6]
|
||||
doc.gsub!(/\n#{indent}/, "\\n")
|
||||
doc.gsub!('"', '\\"')
|
||||
token(:STRING, "\"#{doc}\"")
|
||||
@line += match[1].count("\n")
|
||||
@i += match[1].length
|
||||
end
|
||||
|
||||
# Matches interpolated JavaScript.
|
||||
def js_token
|
||||
return false unless script = @chunk[JS, 1]
|
||||
token(:JS, script.gsub(JS_CLEANER, ''))
|
||||
@i += script.length
|
||||
end
|
||||
|
||||
# Matches regular expression literals.
|
||||
def regex_token
|
||||
return false unless regex = @chunk[REGEX, 1]
|
||||
return false if NOT_REGEX.include?(last_tag)
|
||||
token(:REGEX, regex)
|
||||
@i += regex.length
|
||||
end
|
||||
|
||||
# Matches and consumes comments.
|
||||
def comment_token
|
||||
return false unless comment = @chunk[COMMENT, 1]
|
||||
@line += comment.scan(MULTILINER).length
|
||||
token(:COMMENT, comment.gsub(COMMENT_CLEANER, '').split(MULTILINER))
|
||||
token("\n", "\n")
|
||||
@i += comment.length
|
||||
end
|
||||
|
||||
# Record tokens for indentation differing from the previous line.
|
||||
def indent_token
|
||||
return false unless indent = @chunk[MULTI_DENT, 1]
|
||||
@line += indent.scan(MULTILINER).size
|
||||
@i += indent.size
|
||||
next_character = @chunk[MULTI_DENT, 4]
|
||||
no_newlines = next_character == '.' || (last_value.to_s.match(NO_NEWLINE) && last_value != "=>")
|
||||
return suppress_newlines(indent) if no_newlines
|
||||
size = indent.scan(LAST_DENT).last.last.length
|
||||
return newline_token(indent) if size == @indent
|
||||
if size > @indent
|
||||
token(:INDENT, size - @indent)
|
||||
@indents << (size - @indent)
|
||||
else
|
||||
outdent_token(@indent - size)
|
||||
end
|
||||
@indent = size
|
||||
end
|
||||
|
||||
# Record an oudent token or tokens, if we're moving back inwards past
|
||||
# multiple recorded indents.
|
||||
def outdent_token(move_out)
|
||||
while move_out > 0 && !@indents.empty?
|
||||
last_indent = @indents.pop
|
||||
token(:OUTDENT, last_indent)
|
||||
move_out -= last_indent
|
||||
end
|
||||
token("\n", "\n")
|
||||
end
|
||||
|
||||
# Matches and consumes non-meaningful whitespace.
|
||||
def whitespace_token
|
||||
return false unless whitespace = @chunk[WHITESPACE, 1]
|
||||
@i += whitespace.length
|
||||
end
|
||||
|
||||
# Multiple newlines get merged together.
|
||||
# Use a trailing \ to escape newlines.
|
||||
def newline_token(newlines)
|
||||
token("\n", "\n") unless last_value == "\n"
|
||||
true
|
||||
end
|
||||
|
||||
# Tokens to explicitly escape newlines are removed once their job is done.
|
||||
def suppress_newlines(newlines)
|
||||
@tokens.pop if last_value == "\\"
|
||||
true
|
||||
end
|
||||
|
||||
# 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.
|
||||
def literal_token
|
||||
value = @chunk[OPERATOR, 1]
|
||||
tag_parameters if value && value.match(CODE)
|
||||
value ||= @chunk[0,1]
|
||||
tag = value.match(ASSIGNMENT) ? :ASSIGN : value
|
||||
token(tag, value)
|
||||
@i += value.length
|
||||
end
|
||||
|
||||
# Helpers ==========================================================
|
||||
|
||||
# Add a token to the results, taking note of the line number, and
|
||||
# immediately-preceding comment.
|
||||
def token(tag, value)
|
||||
@tokens << [tag, Value.new(value, @line)]
|
||||
end
|
||||
|
||||
# Peek at the previous token's value.
|
||||
def last_value
|
||||
@tokens.last && @tokens.last[1]
|
||||
end
|
||||
|
||||
# Peek at the previous token's tag.
|
||||
def last_tag
|
||||
@tokens.last && @tokens.last[0]
|
||||
end
|
||||
|
||||
# A source of ambiguity in our grammar was parameter lists in function
|
||||
# definitions (as opposed to argument lists in function calls). Tag
|
||||
# parameter identifiers in order to avoid this. Also, parameter lists can
|
||||
# make use of splats.
|
||||
def tag_parameters
|
||||
i = 0
|
||||
loop do
|
||||
i -= 1
|
||||
tok = @tokens[i]
|
||||
return if !tok
|
||||
next if ['.', ','].include?(tok[0])
|
||||
return if tok[0] != :IDENTIFIER
|
||||
tok[0] = :PARAM
|
||||
end
|
||||
end
|
||||
|
||||
# Close up all remaining open blocks. IF the first token is an indent,
|
||||
# axe it.
|
||||
def close_indentation
|
||||
outdent_token(@indent)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
62
lib/coffee_script/narwhal/coffee-script.coffee
Normal file
62
lib/coffee_script/narwhal/coffee-script.coffee
Normal file
@@ -0,0 +1,62 @@
|
||||
# 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 =>
|
||||
if args.length
|
||||
for path, i in args
|
||||
exports.evalCS(File.read(path))
|
||||
delete args[i]
|
||||
return true
|
||||
|
||||
while true
|
||||
try
|
||||
system.stdout.write('coffee> ').flush()
|
||||
result: exports.evalCS(Readline.readline(), ['--globals'])
|
||||
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, flags =>
|
||||
coffee: OS.popen([coffeePath, "--eval", "--no-wrap"].concat(flags or []))
|
||||
coffee.stdin.write(source).flush().close()
|
||||
checkForErrors(coffee)
|
||||
coffee.stdout.read()
|
||||
|
||||
# Evaluating a string of CoffeeScript first compiles it externally.
|
||||
exports.evalCS: source, flags =>
|
||||
eval(exports.compile(source, flags))
|
||||
|
||||
# Make a factory for the CoffeeScript environment.
|
||||
exports.makeNarwhalFactory: path =>
|
||||
code: exports.compileFile(path)
|
||||
factoryText: "function(require,exports,module,system,print){" + code + "/**/\n}"
|
||||
if system.engine is "rhino"
|
||||
Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null)
|
||||
else
|
||||
# eval requires parentheses, but parentheses break compileFunction.
|
||||
eval("(" + factoryText + ")")
|
||||
77
lib/coffee_script/narwhal/lib/coffee-script.js
Normal file
77
lib/coffee_script/narwhal/lib/coffee-script.js
Normal file
@@ -0,0 +1,77 @@
|
||||
(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 checkForErrors(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 run(args) {
|
||||
var __a, i, path, result;
|
||||
if (args.length) {
|
||||
__a = args;
|
||||
for (i=0; i<__a.length; i++) {
|
||||
path = __a[i];
|
||||
exports.evalCS(File.read(path));
|
||||
delete args[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
system.stdout.write('coffee> ').flush();
|
||||
result = exports.evalCS(Readline.readline(), ['--globals']);
|
||||
if (result !== undefined) {
|
||||
print(result);
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
// Compile a given CoffeeScript file into JavaScript.
|
||||
exports.compileFile = function compileFile(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 compile(source, flags) {
|
||||
var coffee;
|
||||
coffee = OS.popen([coffeePath, "--eval", "--no-wrap"].concat(flags || []));
|
||||
coffee.stdin.write(source).flush().close();
|
||||
checkForErrors(coffee);
|
||||
return coffee.stdout.read();
|
||||
};
|
||||
// Evaluating a string of CoffeeScript first compiles it externally.
|
||||
exports.evalCS = function evalCS(source, flags) {
|
||||
return eval(exports.compile(source, flags));
|
||||
};
|
||||
// Make a factory for the CoffeeScript environment.
|
||||
exports.makeNarwhalFactory = function makeNarwhalFactory(path) {
|
||||
var code, factoryText;
|
||||
code = exports.compileFile(path);
|
||||
factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
|
||||
if (system.engine === "rhino") {
|
||||
return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
|
||||
} else {
|
||||
// eval requires parentheses, but parentheses break compileFunction.
|
||||
return eval("(" + factoryText + ")");
|
||||
}
|
||||
};
|
||||
})();
|
||||
21
lib/coffee_script/narwhal/lib/coffee-script/loader.js
Normal file
21
lib/coffee_script/narwhal/lib/coffee-script/loader.js
Normal file
@@ -0,0 +1,21 @@
|
||||
(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 reload(topId, path) {
|
||||
coffeescript = coffeescript || require('coffee-script');
|
||||
return factories[topId] = function() {
|
||||
return coffeescript.makeNarwhalFactory(path);
|
||||
};
|
||||
},
|
||||
// Ensure that the coffee-script environment is loaded.
|
||||
load: function load(topId, path) {
|
||||
return factories[topId] = factories[topId] || this.reload(topId, path);
|
||||
}
|
||||
};
|
||||
require.loader.loaders.unshift([".coffee", loader]);
|
||||
})();
|
||||
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])
|
||||
895
lib/coffee_script/nodes.rb
Normal file
895
lib/coffee_script/nodes.rb
Normal file
@@ -0,0 +1,895 @@
|
||||
module CoffeeScript
|
||||
|
||||
# The abstract base class for all CoffeeScript nodes.
|
||||
# All nodes are implement a "compile_node" method, which performs the
|
||||
# code generation for that node. To compile a node, call the "compile"
|
||||
# method, which wraps "compile_node" in some extra smarts, to know when the
|
||||
# generated code should be wrapped up in a closure. An options hash is passed
|
||||
# and cloned throughout, containing messages from higher in the AST,
|
||||
# information about the current scope, and indentation level.
|
||||
class Node
|
||||
# Tabs are two spaces for pretty-printing.
|
||||
TAB = ' '
|
||||
|
||||
# Tag this node as a statement, meaning that it can't be used directly as
|
||||
# the result of an expression.
|
||||
def self.statement
|
||||
class_eval "def statement?; true; end"
|
||||
end
|
||||
|
||||
# Tag this node as a statement that cannot be transformed into an expression.
|
||||
# (break, continue, etc.) It doesn't make sense to try to transform it.
|
||||
def self.statement_only
|
||||
statement
|
||||
class_eval "def statement_only?; true; end"
|
||||
end
|
||||
|
||||
def write(code)
|
||||
puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE']
|
||||
code
|
||||
end
|
||||
|
||||
# This is extremely important -- we convert JS statements into expressions
|
||||
# by wrapping them in a closure, only if it's possible, and we're not at
|
||||
# the top level of a block (which would be unnecessary), and we haven't
|
||||
# already been asked to return the result.
|
||||
def compile(o={})
|
||||
@options = o.dup
|
||||
@indent = o[:indent]
|
||||
top = self.is_a?(ForNode) ? @options[:top] : @options.delete(:top)
|
||||
closure = statement? && !statement_only? && !top && !@options[:return]
|
||||
closure ? compile_closure(@options) : compile_node(@options)
|
||||
end
|
||||
|
||||
def compile_closure(o={})
|
||||
indent = o[:indent]
|
||||
@indent = (o[:indent] = idt(1))
|
||||
"(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
|
||||
end
|
||||
|
||||
# Quick short method for the current indentation level, plus tabbing in.
|
||||
def idt(tabs=0)
|
||||
@indent + (TAB * tabs)
|
||||
end
|
||||
|
||||
# Default implementations of the common node methods.
|
||||
def unwrap; self; end
|
||||
def statement?; false; end
|
||||
def statement_only?; false; end
|
||||
end
|
||||
|
||||
# A collection of nodes, each one representing an expression.
|
||||
class Expressions < Node
|
||||
statement
|
||||
attr_reader :expressions
|
||||
|
||||
TRAILING_WHITESPACE = /\s+$/
|
||||
UPPERCASE = /[A-Z]/
|
||||
|
||||
# Wrap up a node as an Expressions, unless it already is.
|
||||
def self.wrap(*nodes)
|
||||
return nodes[0] if nodes.length == 1 && nodes[0].is_a?(Expressions)
|
||||
Expressions.new(*nodes)
|
||||
end
|
||||
|
||||
def initialize(*nodes)
|
||||
@expressions = nodes.flatten
|
||||
end
|
||||
|
||||
# Tack an expression on to the end of this expression list.
|
||||
def <<(node)
|
||||
@expressions << node
|
||||
self
|
||||
end
|
||||
|
||||
# Tack an expression on to the beginning of this expression list.
|
||||
def unshift(node)
|
||||
@expressions.unshift(node)
|
||||
self
|
||||
end
|
||||
|
||||
# If this Expressions consists of a single node, pull it back out.
|
||||
def unwrap
|
||||
@expressions.length == 1 ? @expressions.first : self
|
||||
end
|
||||
|
||||
# Is the node last in this block of expressions.
|
||||
def last?(node)
|
||||
@last_index ||= @expressions.last.is_a?(CommentNode) ? -2 : -1
|
||||
node == @expressions[@last_index]
|
||||
end
|
||||
|
||||
# Determine if this is the expressions body within a constructor function.
|
||||
# Constructors are capitalized by CoffeeScript convention.
|
||||
def constructor?(o)
|
||||
o[:top] && o[:last_assign] && o[:last_assign][0..0][UPPERCASE]
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
o[:scope] ? super(o) : compile_root(o)
|
||||
end
|
||||
|
||||
# Compile each expression in the Expressions body.
|
||||
def compile_node(options={})
|
||||
write(@expressions.map {|n| compile_expression(n, options.dup) }.join("\n"))
|
||||
end
|
||||
|
||||
# If this is the top-level Expressions, wrap everything in a safety closure.
|
||||
def compile_root(o={})
|
||||
indent = o[:no_wrap] ? '' : TAB
|
||||
@indent = indent
|
||||
o.merge!(:indent => indent, :scope => Scope.new(nil, self))
|
||||
code = o[:globals] ? compile_node(o) : compile_with_declarations(o)
|
||||
code.gsub!(TRAILING_WHITESPACE, '')
|
||||
write(o[:no_wrap] ? code : "(function(){\n#{code}\n})();")
|
||||
end
|
||||
|
||||
# Compile the expressions body, with declarations of all inner variables
|
||||
# at the top.
|
||||
def compile_with_declarations(o={})
|
||||
code = compile_node(o)
|
||||
return code unless o[:scope].declarations?(self)
|
||||
write("#{idt}var #{o[:scope].declared_variables.join(', ')};\n#{code}")
|
||||
end
|
||||
|
||||
# Compiles a single expression within the expression list.
|
||||
def compile_expression(node, o)
|
||||
@indent = o[:indent]
|
||||
stmt = node.statement?
|
||||
# We need to return the result if this is the last node in the expressions body.
|
||||
returns = o.delete(:return) && last?(node) && !node.statement_only?
|
||||
# Return the regular compile of the node, unless we need to return the result.
|
||||
return "#{stmt ? '' : idt}#{node.compile(o.merge(:top => true))}#{stmt ? '' : ';'}" unless returns
|
||||
# If it's a statement, the node knows how to return itself.
|
||||
return node.compile(o.merge(:return => true)) if node.statement?
|
||||
# If it's not part of a constructor, we can just return the value of the expression.
|
||||
return "#{idt}return #{node.compile(o)};" unless constructor?(o)
|
||||
# It's the last line of a constructor, add a safety check.
|
||||
temp = o[:scope].free_variable
|
||||
"#{idt}#{temp} = #{node.compile(o)};\n#{idt}return #{o[:last_assign]} === this.constructor ? this : #{temp};"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Literals are static values that have a Ruby representation, eg.: a string, a number,
|
||||
# true, false, nil, etc.
|
||||
class LiteralNode < Node
|
||||
|
||||
# Values of a literal node that much be treated as a statement -- no
|
||||
# sense returning or assigning them.
|
||||
STATEMENTS = ['break', 'continue']
|
||||
|
||||
# If we get handed a literal reference to an arguments object, convert
|
||||
# it to an array.
|
||||
ARG_ARRAY = 'Array.prototype.slice.call(arguments, 0)'
|
||||
|
||||
attr_reader :value
|
||||
|
||||
# Wrap up a compiler-generated string as a LiteralNode.
|
||||
def self.wrap(string)
|
||||
self.new(Value.new(string))
|
||||
end
|
||||
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
|
||||
def statement?
|
||||
STATEMENTS.include?(@value.to_s)
|
||||
end
|
||||
alias_method :statement_only?, :statement?
|
||||
|
||||
def compile_node(o)
|
||||
@value = ARG_ARRAY if @value.to_s.to_sym == :arguments
|
||||
indent = statement? ? idt : ''
|
||||
ending = statement? ? ';' : ''
|
||||
write "#{indent}#{@value}#{ending}"
|
||||
end
|
||||
end
|
||||
|
||||
# Return an expression, or wrap it in a closure and return it.
|
||||
class ReturnNode < Node
|
||||
statement_only
|
||||
|
||||
attr_reader :expression
|
||||
|
||||
def initialize(expression)
|
||||
@expression = expression
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
return write(@expression.compile(o.merge(:return => true))) if @expression.statement?
|
||||
compiled = @expression.compile(o)
|
||||
write(@expression.statement? ? "#{compiled}\n#{idt}return null;" : "#{idt}return #{compiled};")
|
||||
end
|
||||
end
|
||||
|
||||
# Pass through CoffeeScript comments into JavaScript comments at the
|
||||
# same position.
|
||||
class CommentNode < Node
|
||||
statement_only
|
||||
|
||||
def initialize(lines)
|
||||
@lines = lines.value
|
||||
end
|
||||
|
||||
def compile_node(o={})
|
||||
delimiter = "\n#{idt}//"
|
||||
write("#{delimiter}#{@lines.join(delimiter)}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Node for a function invocation. Takes care of converting super() calls into
|
||||
# calls against the prototype's function of the same name.
|
||||
class CallNode < Node
|
||||
attr_reader :variable, :arguments
|
||||
|
||||
def initialize(variable, arguments=[])
|
||||
@variable, @arguments = variable, arguments
|
||||
end
|
||||
|
||||
def new_instance
|
||||
@new = true
|
||||
self
|
||||
end
|
||||
|
||||
def super?
|
||||
@variable == :super
|
||||
end
|
||||
|
||||
def prefix
|
||||
@new ? "new " : ''
|
||||
end
|
||||
|
||||
def splat?
|
||||
@arguments.any? {|a| a.is_a?(ArgSplatNode) }
|
||||
end
|
||||
|
||||
def <<(argument)
|
||||
@arguments << argument
|
||||
end
|
||||
|
||||
# Compile a vanilla function call.
|
||||
def compile_node(o)
|
||||
return write(compile_splat(o)) if splat?
|
||||
args = @arguments.map{|a| a.compile(o) }.join(', ')
|
||||
return write(compile_super(args, o)) if super?
|
||||
write("#{prefix}#{@variable.compile(o)}(#{args})")
|
||||
end
|
||||
|
||||
# Compile a call against the superclass's implementation of the current function.
|
||||
def compile_super(args, o)
|
||||
methname = o[:last_assign]
|
||||
arg_part = args.empty? ? '' : ", #{args}"
|
||||
meth = o[:proto_assign] ? "#{o[:proto_assign]}.__superClass__.#{methname}" :
|
||||
"#{methname}.__superClass__.constructor"
|
||||
"#{meth}.call(this#{arg_part})"
|
||||
end
|
||||
|
||||
# Compile a function call being passed variable arguments.
|
||||
def compile_splat(o)
|
||||
meth = @variable.compile(o)
|
||||
obj = @variable.source || 'this'
|
||||
args = @arguments.map do |arg|
|
||||
code = arg.compile(o)
|
||||
code = arg.is_a?(ArgSplatNode) ? code : "[#{code}]"
|
||||
arg.equal?(@arguments.first) ? code : ".concat(#{code})"
|
||||
end
|
||||
"#{prefix}#{meth}.apply(#{obj}, #{args.join('')})"
|
||||
end
|
||||
end
|
||||
|
||||
# Node to extend an object's prototype with an ancestor object.
|
||||
# After goog.inherits from the Closure Library.
|
||||
class ExtendsNode < Node
|
||||
statement
|
||||
attr_reader :sub_object, :super_object
|
||||
|
||||
def initialize(sub_object, super_object)
|
||||
@sub_object, @super_object = sub_object, super_object
|
||||
end
|
||||
|
||||
# Hooking one constructor into another's prototype chain.
|
||||
def compile_node(o={})
|
||||
constructor = o[:scope].free_variable
|
||||
sub, sup = @sub_object.compile(o), @super_object.compile(o)
|
||||
"#{idt}#{constructor} = function(){};\n#{idt}" +
|
||||
"#{constructor}.prototype = #{sup}.prototype;\n#{idt}" +
|
||||
"#{sub}.__superClass__ = #{sup}.prototype;\n#{idt}" +
|
||||
"#{sub}.prototype = new #{constructor}();\n#{idt}" +
|
||||
"#{sub}.prototype.constructor = #{sub};"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A value, indexed or dotted into, or vanilla.
|
||||
class ValueNode < Node
|
||||
attr_reader :base, :properties, :last, :source
|
||||
|
||||
def initialize(base, properties=[])
|
||||
@base, @properties = base, properties
|
||||
end
|
||||
|
||||
def <<(other)
|
||||
@properties << other
|
||||
self
|
||||
end
|
||||
|
||||
def properties?
|
||||
return !@properties.empty?
|
||||
end
|
||||
|
||||
def array?
|
||||
@base.is_a?(ArrayNode) && !properties?
|
||||
end
|
||||
|
||||
def object?
|
||||
@base.is_a?(ObjectNode) && !properties?
|
||||
end
|
||||
|
||||
def splice?
|
||||
properties? && @properties.last.is_a?(SliceNode)
|
||||
end
|
||||
|
||||
# Values are statements if their base is a statement.
|
||||
def statement?
|
||||
@base.is_a?(Node) && @base.statement? && !properties?
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
only = o.delete(:only_first)
|
||||
props = only ? @properties[0...-1] : @properties
|
||||
parts = [@base, props].flatten.map {|val| val.compile(o) }
|
||||
@last = parts.last
|
||||
@source = parts.length > 1 ? parts[0...-1].join('') : nil
|
||||
write(parts.join(''))
|
||||
end
|
||||
end
|
||||
|
||||
# A dotted accessor into a part of a value, or the :: shorthand for
|
||||
# an accessor into the object's prototype.
|
||||
class AccessorNode < Node
|
||||
attr_reader :name
|
||||
|
||||
def initialize(name, prototype=false)
|
||||
@name, @prototype = name, prototype
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
proto = @prototype ? "prototype." : ''
|
||||
write(".#{proto}#{@name}")
|
||||
end
|
||||
end
|
||||
|
||||
# An indexed accessor into a part of an array or object.
|
||||
class IndexNode < Node
|
||||
attr_reader :index
|
||||
|
||||
def initialize(index)
|
||||
@index = index
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
write("[#{@index.compile(o)}]")
|
||||
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 < Node
|
||||
attr_reader :from, :to
|
||||
|
||||
def initialize(from, to, exclusive=false)
|
||||
@from, @to, @exclusive = from, to, exclusive
|
||||
end
|
||||
|
||||
def exclusive?
|
||||
@exclusive
|
||||
end
|
||||
|
||||
def compile_variables(o)
|
||||
@indent = o[:indent]
|
||||
@from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
|
||||
from_val, to_val = @from.compile(o), @to.compile(o)
|
||||
write("#{@from_var} = #{from_val}; #{@to_var} = #{to_val};\n#{idt}")
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
return compile_array(o) unless o[:index]
|
||||
idx, step = o.delete(:index), o.delete(:step)
|
||||
vars = "#{idx}=#{@from_var}"
|
||||
step = step ? step.compile(o) : '1'
|
||||
equals = @exclusive ? '' : '='
|
||||
compare = "(#{@from_var} <= #{@to_var} ? #{idx} <#{equals} #{@to_var} : #{idx} >#{equals} #{@to_var})"
|
||||
incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
|
||||
write("#{vars}; #{compare}; #{incr}")
|
||||
end
|
||||
|
||||
# Expand the range into the equivalent array, if it's not being used as
|
||||
# part of a comprehension, slice, or splice.
|
||||
# TODO: This generates pretty ugly code ... shrink it.
|
||||
def compile_array(o)
|
||||
body = Expressions.wrap(LiteralNode.wrap('i'))
|
||||
arr = Expressions.wrap(ForNode.new(body, {:source => ValueNode.new(self)}, Value.new('i')))
|
||||
ParentheticalNode.new(CallNode.new(CodeNode.new([], arr))).compile(o)
|
||||
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 :range
|
||||
|
||||
def initialize(range)
|
||||
@range = range
|
||||
end
|
||||
|
||||
def compile_node(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
|
||||
PROTO_ASSIGN = /\A(\S+)\.prototype/
|
||||
LEADING_DOT = /\A\.(prototype\.)?/
|
||||
|
||||
attr_reader :variable, :value, :context
|
||||
|
||||
def initialize(variable, value, context=nil)
|
||||
@variable, @value, @context = variable, value, context
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
return compile_pattern_match(o) if @variable.array? || @variable.object?
|
||||
return compile_splice(o) if @variable.splice?
|
||||
stmt = o.delete(:as_statement)
|
||||
name = @variable.compile(o)
|
||||
last = @variable.last.to_s.sub(LEADING_DOT, '')
|
||||
proto = name[PROTO_ASSIGN, 1]
|
||||
o = o.merge(:last_assign => last, :proto_assign => proto)
|
||||
o[:immediate_assign] = last if @value.is_a?(CodeNode) && last.match(Lexer::IDENTIFIER)
|
||||
return write("#{name}: #{@value.compile(o)}") if @context == :object
|
||||
o[:scope].find(name) unless @variable.properties?
|
||||
val = "#{name} = #{@value.compile(o)}"
|
||||
return write("#{idt}#{val};") if stmt
|
||||
write(o[:return] ? "#{idt}return (#{val})" : val)
|
||||
end
|
||||
|
||||
def statement?
|
||||
@variable.array? || @variable.object?
|
||||
end
|
||||
|
||||
# Implementation of recursive pattern matching, when assigning array or
|
||||
# object literals to a value. Peeks at their properties to assign inner names.
|
||||
# See: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring
|
||||
def compile_pattern_match(o)
|
||||
val_var = o[:scope].free_variable
|
||||
assigns = ["#{idt}#{val_var} = #{@value.compile(o)};"]
|
||||
o.merge!(:top => true, :as_statement => true)
|
||||
@variable.base.objects.each_with_index do |obj, i|
|
||||
obj, i = obj.value, obj.variable.base if @variable.object?
|
||||
access_class = @variable.array? ? IndexNode : AccessorNode
|
||||
assigns << AssignNode.new(
|
||||
obj, ValueNode.new(Value.new(val_var), [access_class.new(Value.new(i.to_s))])
|
||||
).compile(o)
|
||||
end
|
||||
write(assigns.join("\n"))
|
||||
end
|
||||
|
||||
def compile_splice(o)
|
||||
var = @variable.compile(o.merge(:only_first => true))
|
||||
range = @variable.properties.last.range
|
||||
plus = range.exclusive? ? '' : ' + 1'
|
||||
from = range.from.compile(o)
|
||||
to = "#{range.to.compile(o)} - #{from}#{plus}"
|
||||
write("#{var}.splice.apply(#{var}, [#{from}, #{to}].concat(#{@value.compile(o)}))")
|
||||
end
|
||||
end
|
||||
|
||||
# Simple Arithmetic and logical operations. Performs some conversion from
|
||||
# CoffeeScript operations into their JavaScript equivalents.
|
||||
class OpNode < Node
|
||||
CONVERSIONS = {
|
||||
:== => "===",
|
||||
:'!=' => "!==",
|
||||
:and => '&&',
|
||||
:or => '||',
|
||||
:is => '===',
|
||||
:isnt => "!==",
|
||||
:not => '!'
|
||||
}
|
||||
CONDITIONALS = [:'||=', :'&&=']
|
||||
PREFIX_OPERATORS = [:typeof, :delete]
|
||||
|
||||
attr_reader :operator, :first, :second
|
||||
|
||||
def initialize(operator, first, second=nil, flip=false)
|
||||
@first, @second, @flip = first, second, flip
|
||||
@operator = CONVERSIONS[operator.to_sym] || operator
|
||||
end
|
||||
|
||||
def unary?
|
||||
@second.nil?
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
return write(compile_conditional(o)) if CONDITIONALS.include?(@operator.to_sym)
|
||||
return write(compile_unary(o)) if unary?
|
||||
write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}")
|
||||
end
|
||||
|
||||
def compile_conditional(o)
|
||||
first, second = @first.compile(o), @second.compile(o)
|
||||
sym = @operator[0..1]
|
||||
"#{first} = #{first} #{sym} #{second}"
|
||||
end
|
||||
|
||||
def compile_unary(o)
|
||||
space = PREFIX_OPERATORS.include?(@operator.to_sym) ? ' ' : ''
|
||||
parts = [@operator.to_s, space, @first.compile(o)]
|
||||
parts.reverse! if @flip
|
||||
parts.join('')
|
||||
end
|
||||
end
|
||||
|
||||
# A function definition. The only node that creates a new Scope.
|
||||
class CodeNode < Node
|
||||
attr_reader :params, :body
|
||||
|
||||
def initialize(params, body)
|
||||
@params = params
|
||||
@body = body
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
shared_scope = o.delete(:shared_scope)
|
||||
o[:scope] = shared_scope || Scope.new(o[:scope], @body)
|
||||
o[:return] = true
|
||||
o[:top] = true
|
||||
o[:indent] = idt(1)
|
||||
o.delete(:no_wrap)
|
||||
o.delete(:globals)
|
||||
name = o.delete(:immediate_assign)
|
||||
if @params.last.is_a?(ParamSplatNode)
|
||||
splat = @params.pop
|
||||
splat.index = @params.length
|
||||
@body.unshift(splat)
|
||||
end
|
||||
@params.each {|id| o[:scope].parameter(id.to_s) }
|
||||
code = @body.compile_with_declarations(o)
|
||||
name_part = name ? " #{name}" : ''
|
||||
write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{idt}}")
|
||||
end
|
||||
end
|
||||
|
||||
# A parameter splat in a function definition.
|
||||
class ParamSplatNode < Node
|
||||
attr_accessor :index
|
||||
attr_reader :name
|
||||
|
||||
def initialize(name)
|
||||
@name = name
|
||||
end
|
||||
|
||||
def compile_node(o={})
|
||||
o[:scope].find(@name)
|
||||
write("#{@name} = Array.prototype.slice.call(arguments, #{@index})")
|
||||
end
|
||||
end
|
||||
|
||||
class ArgSplatNode < Node
|
||||
attr_reader :value
|
||||
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
|
||||
def compile_node(o={})
|
||||
write(@value.compile(o))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# An object literal.
|
||||
class ObjectNode < Node
|
||||
attr_reader :properties
|
||||
alias_method :objects, :properties
|
||||
|
||||
def initialize(properties = [])
|
||||
@properties = properties
|
||||
end
|
||||
|
||||
# All the mucking about with commas is to make sure that CommentNodes and
|
||||
# AssignNodes get interleaved correctly, with no trailing commas or
|
||||
# commas affixed to comments. TODO: Extract this and add it to ArrayNode.
|
||||
def compile_node(o)
|
||||
o[:indent] = idt(1)
|
||||
joins = Hash.new("\n")
|
||||
non_comments = @properties.select {|p| !p.is_a?(CommentNode) }
|
||||
non_comments.each {|p| joins[p] = p == non_comments.last ? "\n" : ",\n" }
|
||||
props = @properties.map { |prop|
|
||||
join = joins[prop]
|
||||
join = '' if prop == @properties.last
|
||||
indent = prop.is_a?(CommentNode) ? '' : idt(1)
|
||||
"#{indent}#{prop.compile(o)}#{join}"
|
||||
}.join('')
|
||||
write("{\n#{props}\n#{idt}}")
|
||||
end
|
||||
end
|
||||
|
||||
# An array literal.
|
||||
class ArrayNode < Node
|
||||
attr_reader :objects
|
||||
|
||||
def initialize(objects=[])
|
||||
@objects = objects
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
o[:indent] = idt(1)
|
||||
objects = @objects.map { |obj|
|
||||
code = obj.compile(o)
|
||||
obj.is_a?(CommentNode) ? "\n#{code}\n#{o[:indent]}" :
|
||||
obj == @objects.last ? code : "#{code}, "
|
||||
}.join('')
|
||||
ending = objects.include?("\n") ? "\n#{idt}]" : ']'
|
||||
write("[#{objects}#{ending}")
|
||||
end
|
||||
end
|
||||
|
||||
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||||
# it, all other loops can be manufactured.
|
||||
class WhileNode < Node
|
||||
statement
|
||||
|
||||
attr_reader :condition, :body
|
||||
|
||||
def initialize(condition, body)
|
||||
@condition, @body = condition, body
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
returns = o.delete(:return)
|
||||
o[:indent] = idt(1)
|
||||
o[:top] = true
|
||||
cond = @condition.compile(o)
|
||||
post = returns ? "\n#{idt}return null;" : ''
|
||||
return write("#{idt}while (#{cond}) null;#{post}") if @body.nil?
|
||||
write("#{idt}while (#{cond}) {\n#{@body.compile(o)}\n#{idt}}#{post}")
|
||||
end
|
||||
end
|
||||
|
||||
# The replacement for the for loop is an array comprehension (that compiles)
|
||||
# into a for loop. Also acts as an expression, able to return the result
|
||||
# of the comprehenion. Unlike Python array comprehensions, it's able to pass
|
||||
# the current index of the loop as a second parameter.
|
||||
class ForNode < Node
|
||||
statement
|
||||
|
||||
attr_reader :body, :source, :name, :index, :filter, :step
|
||||
|
||||
def initialize(body, source, name, index=nil)
|
||||
@body, @name, @index = body, name, index
|
||||
@source = source[:source]
|
||||
@filter = source[:filter]
|
||||
@step = source[:step]
|
||||
@object = !!source[:object]
|
||||
@name, @index = @index, @name if @object
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
top_level = o.delete(:top) && !o[:return]
|
||||
range = @source.is_a?(ValueNode) && @source.base.is_a?(RangeNode) && @source.properties.empty?
|
||||
source = range ? @source.base : @source
|
||||
scope = o[:scope]
|
||||
name_found = @name && scope.find(@name)
|
||||
index_found = @index && scope.find(@index)
|
||||
body_dent = idt(1)
|
||||
rvar = scope.free_variable unless top_level
|
||||
svar = scope.free_variable
|
||||
ivar = range ? name : @index ? @index : scope.free_variable
|
||||
if range
|
||||
index_var = scope.free_variable
|
||||
source_part = source.compile_variables(o)
|
||||
for_part = "#{index_var}=0, #{source.compile(o.merge(:index => ivar, :step => @step))}, #{index_var}++"
|
||||
var_part = ''
|
||||
else
|
||||
index_var = nil
|
||||
source_part = "#{svar} = #{source.compile(o)};\n#{idt}"
|
||||
for_part = @object ? "#{ivar} in #{svar}" : "#{ivar}=0; #{ivar}<#{svar}.length; #{ivar}++"
|
||||
var_part = @name ? "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" : ''
|
||||
end
|
||||
body = @body
|
||||
set_result = rvar ? "#{idt}#{rvar} = []; " : idt
|
||||
return_result = rvar || ''
|
||||
if top_level
|
||||
body = Expressions.wrap(body)
|
||||
else
|
||||
body = Expressions.wrap(CallNode.new(
|
||||
ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [body.unwrap]
|
||||
))
|
||||
end
|
||||
if o[:return]
|
||||
return_result = "return #{return_result}" if o[:return]
|
||||
o.delete(:return)
|
||||
body = IfNode.new(@filter, body, nil, :statement => true) if @filter
|
||||
elsif @filter
|
||||
body = Expressions.wrap(IfNode.new(@filter, body))
|
||||
end
|
||||
if @object
|
||||
body = Expressions.wrap(IfNode.new(
|
||||
CallNode.new(ValueNode.new(LiteralNode.wrap(svar), [AccessorNode.new(Value.new('hasOwnProperty'))]), [LiteralNode.wrap(ivar)]),
|
||||
Expressions.wrap(body),
|
||||
nil,
|
||||
{:statement => true}
|
||||
))
|
||||
end
|
||||
|
||||
return_result = "\n#{idt}#{return_result};" unless top_level
|
||||
body = body.compile(o.merge(:indent => body_dent, :top => true))
|
||||
vars = range ? @name : "#{@name}, #{ivar}"
|
||||
return write(set_result + source_part + "for (#{for_part}) {\n#{var_part}#{body}\n#{idt}}\n#{idt}#{return_result}")
|
||||
end
|
||||
end
|
||||
|
||||
# A try/catch/finally block.
|
||||
class TryNode < Node
|
||||
statement
|
||||
|
||||
attr_reader :try, :error, :recovery, :finally
|
||||
|
||||
def initialize(try, error, recovery, finally=nil)
|
||||
@try, @error, @recovery, @finally = try, error, recovery, finally
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
o[:indent] = idt(1)
|
||||
o[:top] = true
|
||||
error_part = @error ? " (#{@error}) " : ' '
|
||||
catch_part = @recovery && " catch#{error_part}{\n#{@recovery.compile(o)}\n#{idt}}"
|
||||
finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:return => nil))}\n#{idt}}"
|
||||
write("#{idt}try {\n#{@try.compile(o)}\n#{idt}}#{catch_part}#{finally_part}")
|
||||
end
|
||||
end
|
||||
|
||||
# Throw an exception.
|
||||
class ThrowNode < Node
|
||||
statement_only
|
||||
|
||||
attr_reader :expression
|
||||
|
||||
def initialize(expression)
|
||||
@expression = expression
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
write("#{idt}throw #{@expression.compile(o)};")
|
||||
end
|
||||
end
|
||||
|
||||
# Check an expression for existence (meaning not null or undefined).
|
||||
class ExistenceNode < Node
|
||||
attr_reader :expression
|
||||
|
||||
def initialize(expression)
|
||||
@expression = expression
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
val = @expression.compile(o)
|
||||
write("(typeof #{val} !== \"undefined\" && #{val} !== null)")
|
||||
end
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
def initialize(expressions, line=nil)
|
||||
@expressions = expressions.unwrap
|
||||
@line = line
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
compiled = @expressions.compile(o)
|
||||
compiled = compiled[0...-1] if compiled[-1..-1] == ';'
|
||||
write("(#{compiled})")
|
||||
end
|
||||
end
|
||||
|
||||
# If/else statements. Switch/whens get compiled into these. Acts as an
|
||||
# expression by pushing down requested returns to the expression bodies.
|
||||
# Single-expression IfNodes are compiled into ternary operators if possible,
|
||||
# because ternaries are first-class returnable assignable expressions.
|
||||
class IfNode < Node
|
||||
attr_reader :condition, :body, :else_body
|
||||
|
||||
def initialize(condition, body, else_body=nil, tags={})
|
||||
@condition = condition
|
||||
@body = body && body.unwrap
|
||||
@else_body = else_body && else_body.unwrap
|
||||
@tags = tags
|
||||
@condition = OpNode.new("!", ParentheticalNode.new(@condition)) if @tags[:invert]
|
||||
end
|
||||
|
||||
def <<(else_body)
|
||||
eb = else_body.unwrap
|
||||
@else_body ? @else_body << eb : @else_body = eb
|
||||
self
|
||||
end
|
||||
|
||||
def add_comment(comment)
|
||||
@comment = comment
|
||||
self
|
||||
end
|
||||
|
||||
def force_statement
|
||||
@tags[:statement] = true
|
||||
self
|
||||
end
|
||||
|
||||
# Rewrite a chain of IfNodes with their switch condition for equality.
|
||||
def rewrite_condition(expression)
|
||||
@condition = OpNode.new("is", expression, @condition)
|
||||
@else_body.rewrite_condition(expression) if chain?
|
||||
self
|
||||
end
|
||||
|
||||
# Rewrite a chain of IfNodes to add a default case as the final else.
|
||||
def add_else(exprs)
|
||||
chain? ? @else_body.add_else(exprs) : @else_body = (exprs && exprs.unwrap)
|
||||
self
|
||||
end
|
||||
|
||||
# If the else_body is an IfNode itself, then we've got an if-else chain.
|
||||
def chain?
|
||||
@chain ||= @else_body && @else_body.is_a?(IfNode)
|
||||
end
|
||||
|
||||
# The IfNode only compiles into a statement if either of the bodies needs
|
||||
# to be a statement.
|
||||
def statement?
|
||||
@is_statement ||= !!(@comment || @tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
|
||||
end
|
||||
|
||||
def compile_node(o)
|
||||
write(statement? ? compile_statement(o) : compile_ternary(o))
|
||||
end
|
||||
|
||||
# Compile the IfNode as a regular if-else statement. Flattened chains
|
||||
# force sub-else bodies into statement form.
|
||||
def compile_statement(o)
|
||||
child = o.delete(:chain_child)
|
||||
cond_o = o.dup
|
||||
cond_o.delete(:return)
|
||||
o[:indent] = idt(1)
|
||||
o[:top] = true
|
||||
if_dent = child ? '' : idt
|
||||
com_dent = child ? idt : ''
|
||||
prefix = @comment ? @comment.compile(cond_o) + "\n#{com_dent}" : ''
|
||||
if_part = "#{prefix}#{if_dent}if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{idt}}"
|
||||
return if_part unless @else_body
|
||||
else_part = chain? ?
|
||||
" else #{@else_body.compile(o.merge(:indent => idt, :chain_child => true))}" :
|
||||
" else {\n#{Expressions.wrap(@else_body).compile(o)}\n#{idt}}"
|
||||
if_part + else_part
|
||||
end
|
||||
|
||||
# Compile the IfNode into a ternary operator.
|
||||
def compile_ternary(o)
|
||||
if_part = "#{@condition.compile(o)} ? #{@body.compile(o)}"
|
||||
else_part = @else_body ? "#{@else_body.compile(o)}" : 'null'
|
||||
"#{if_part} : #{else_part}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
23
lib/coffee_script/parse_error.rb
Normal file
23
lib/coffee_script/parse_error.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
module CoffeeScript
|
||||
|
||||
# Racc will raise this Exception whenever a syntax error occurs. The main
|
||||
# benefit over the Racc::ParseError is that the CoffeeScript::ParseError is
|
||||
# line-number aware.
|
||||
class ParseError < Racc::ParseError
|
||||
|
||||
def initialize(token_id, value, stack)
|
||||
@token_id, @value, @stack = token_id, value, stack
|
||||
end
|
||||
|
||||
def message
|
||||
line = @value.respond_to?(:line) ? @value.line : "END"
|
||||
line_part = "line #{line}:"
|
||||
id_part = @token_id != @value.inspect ? ", unexpected #{@token_id.to_s.downcase}" : ""
|
||||
val_part = ['INDENT', 'OUTDENT'].include?(@token_id) ? '' : " for '#{@value.to_s}'"
|
||||
"#{line_part} syntax error#{val_part}#{id_part}"
|
||||
end
|
||||
alias_method :inspect, :message
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user