mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-14 09:17:55 -05:00
Compare commits
268 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e645cce41 | ||
|
|
4f564dec60 | ||
|
|
20144abe25 | ||
|
|
a8631dcbaf | ||
|
|
f5cd6578b8 | ||
|
|
f1af50ee33 | ||
|
|
ad5e69d303 | ||
|
|
7d800b5e5b | ||
|
|
f150011d9d | ||
|
|
a09f807ce0 | ||
|
|
6082b9dc36 | ||
|
|
326904826d | ||
|
|
3f30712ca1 | ||
|
|
e9b72ee955 | ||
|
|
7be3b8edac | ||
|
|
3daac200e5 | ||
|
|
f77877d7eb | ||
|
|
536bdd2107 | ||
|
|
0093455d29 | ||
|
|
67d34ec40b | ||
|
|
d8603dbff2 | ||
|
|
6b6cb3ab12 | ||
|
|
10f53bac15 | ||
|
|
15f12423c3 | ||
|
|
885dbaf7e4 | ||
|
|
0d0df09c6d | ||
|
|
7df7ff26c8 | ||
|
|
841258b360 | ||
|
|
f715393b06 | ||
|
|
abfe3c35f8 | ||
|
|
8517455746 | ||
|
|
0e86fa534e | ||
|
|
1dbf257df7 | ||
|
|
5cbd94bf14 | ||
|
|
fa3f3e41d4 | ||
|
|
94bab256b4 | ||
|
|
dfa2f50076 | ||
|
|
e2de88544d | ||
|
|
010a2dde11 | ||
|
|
2fb8f48e75 | ||
|
|
271ea24b20 | ||
|
|
792fd359bd | ||
|
|
acf4a5ee47 | ||
|
|
d9afe2cf44 | ||
|
|
ee304485b8 | ||
|
|
5957ba624a | ||
|
|
2b40607b57 | ||
|
|
8da4185bbf | ||
|
|
e8df8abf7a | ||
|
|
b7ef9510c9 | ||
|
|
fba131c5a4 | ||
|
|
4985a400e6 | ||
|
|
2ed041b5e1 | ||
|
|
5bcb2b2629 | ||
|
|
adca8183de | ||
|
|
c187f2160f | ||
|
|
959c9a31cb | ||
|
|
9f6233473e | ||
|
|
32b0f9fa4f | ||
|
|
f7e49eaae4 | ||
|
|
3042a50f87 | ||
|
|
c8e820e851 | ||
|
|
66f92e770d | ||
|
|
f3472b7437 | ||
|
|
fdf2a76c53 | ||
|
|
d11569b434 | ||
|
|
86be82f3ae | ||
|
|
bcc5aa2bc3 | ||
|
|
5658b2b41f | ||
|
|
34bf4ce325 | ||
|
|
4251aa30c6 | ||
|
|
3ad9316fd0 | ||
|
|
e4ae324e8f | ||
|
|
370d05148d | ||
|
|
3f27b0ff72 | ||
|
|
71cf6ab031 | ||
|
|
f3e9a18c3c | ||
|
|
d2176acf25 | ||
|
|
62725c5a52 | ||
|
|
cc66b31e69 | ||
|
|
199d314903 | ||
|
|
f2a805db24 | ||
|
|
b7e29aac4d | ||
|
|
2ee04b8421 | ||
|
|
65ce74fbcb | ||
|
|
4b520d04e3 | ||
|
|
4eb10f9d43 | ||
|
|
7e58d1d914 | ||
|
|
4097a81456 | ||
|
|
c4413b933b | ||
|
|
e3da53e3df | ||
|
|
675a5f5d7c | ||
|
|
02c19b3170 | ||
|
|
96859e749b | ||
|
|
622ddea343 | ||
|
|
8e8efe4288 | ||
|
|
df5c5d9fe2 | ||
|
|
cf7ce8a1af | ||
|
|
b3cd5721cf | ||
|
|
893908b8fe | ||
|
|
6821660905 | ||
|
|
bc0214730a | ||
|
|
57d0f25054 | ||
|
|
f456d1b78e | ||
|
|
332f499c31 | ||
|
|
92cdeb093e | ||
|
|
f2bdd555fa | ||
|
|
5c7dee556a | ||
|
|
8c6e5d0b37 | ||
|
|
1128beb49b | ||
|
|
0963eea60e | ||
|
|
c1cdedd260 | ||
|
|
3b0b93ec06 | ||
|
|
9c2f66ff13 | ||
|
|
1b688d7077 | ||
|
|
df1f9c27eb | ||
|
|
e227a3bc69 | ||
|
|
e4c6119550 | ||
|
|
6c9e8f28b6 | ||
|
|
cc7e685428 | ||
|
|
4abd88f2a9 | ||
|
|
a44fe402a1 | ||
|
|
350cb623ae | ||
|
|
42c9c53a4c | ||
|
|
5a49c22121 | ||
|
|
2bc4cbbdcc | ||
|
|
8fe6fa1cd7 | ||
|
|
835db4b279 | ||
|
|
f89c864911 | ||
|
|
542726911a | ||
|
|
575dc7d12e | ||
|
|
ff0062b088 | ||
|
|
78589f5db1 | ||
|
|
903331f3ff | ||
|
|
6aa247f73d | ||
|
|
da71735066 | ||
|
|
47b45c4494 | ||
|
|
d6ac6a3535 | ||
|
|
c322d77b86 | ||
|
|
7aa69579ff | ||
|
|
5f3e2b7fc7 | ||
|
|
c4844abb28 | ||
|
|
96ae6d80f3 | ||
|
|
60342e8cd9 | ||
|
|
f9c3d3fc14 | ||
|
|
b1e25eea88 | ||
|
|
1486bbab9f | ||
|
|
59d912cc26 | ||
|
|
9adf2e2d30 | ||
|
|
cf46fa8c2c | ||
|
|
16ca3d1608 | ||
|
|
476a251c80 | ||
|
|
274152aff7 | ||
|
|
00659e5f76 | ||
|
|
88fe4f6fd1 | ||
|
|
03a90928e1 | ||
|
|
67865d3341 | ||
|
|
0337513172 | ||
|
|
1ee2c53391 | ||
|
|
4b7c965101 | ||
|
|
11c394fb7e | ||
|
|
54a7c405e7 | ||
|
|
781f3b5fa4 | ||
|
|
2393472924 | ||
|
|
859ab7583f | ||
|
|
3eedd5bb50 | ||
|
|
5b7e695f6c | ||
|
|
1e3182727b | ||
|
|
e595dbfcee | ||
|
|
12b830893d | ||
|
|
cca80342aa | ||
|
|
4412f590cf | ||
|
|
0cd2d78027 | ||
|
|
be672ebfc1 | ||
|
|
9047c87567 | ||
|
|
31d630bb91 | ||
|
|
7a2f5a333f | ||
|
|
66303636dc | ||
|
|
9dc932e380 | ||
|
|
a71de4b5b6 | ||
|
|
ada8dfc6d4 | ||
|
|
4112595368 | ||
|
|
c281db7730 | ||
|
|
5e6b49ad1e | ||
|
|
ec1a527575 | ||
|
|
7f066aa168 | ||
|
|
166ea578f9 | ||
|
|
dc0ab1afca | ||
|
|
ae72fbfd0d | ||
|
|
ad0735f765 | ||
|
|
d49c178f1d | ||
|
|
cb3b47690a | ||
|
|
cf6060bdb3 | ||
|
|
8f8ba255b3 | ||
|
|
a4bc24817d | ||
|
|
9eeac9b272 | ||
|
|
f859eb6cec | ||
|
|
b29afc2c09 | ||
|
|
95b79973f9 | ||
|
|
1f79733a33 | ||
|
|
cfc29f7830 | ||
|
|
34486039e1 | ||
|
|
53e8ea7d9e | ||
|
|
9841b78ed8 | ||
|
|
065cfddd0d | ||
|
|
6882a3d36c | ||
|
|
85c595e84c | ||
|
|
b8f563d49e | ||
|
|
3b3d57e84a | ||
|
|
a1ee622aa6 | ||
|
|
64733981fd | ||
|
|
7833b11724 | ||
|
|
a7032d0964 | ||
|
|
f2c872230e | ||
|
|
8d26488748 | ||
|
|
d92ed46503 | ||
|
|
777eac045a | ||
|
|
37e2f3b944 | ||
|
|
3902a8b268 | ||
|
|
63a910d7ce | ||
|
|
3cee51cc37 | ||
|
|
45b559a721 | ||
|
|
7c59eb2c36 | ||
|
|
40f633b8d0 | ||
|
|
4e3d7cb974 | ||
|
|
d14b127b60 | ||
|
|
0d566ed1ec | ||
|
|
d86f92c6f2 | ||
|
|
efc5150144 | ||
|
|
7474ed1a5e | ||
|
|
2f4433af71 | ||
|
|
91303efd2c | ||
|
|
d73ff9a79f | ||
|
|
7ec6300a48 | ||
|
|
42c84fc54b | ||
|
|
f0a790d624 | ||
|
|
fdcff7aaf0 | ||
|
|
ab2362e372 | ||
|
|
98cf9f5af2 | ||
|
|
e74af51a7d | ||
|
|
35b5d8c630 | ||
|
|
8575d91c66 | ||
|
|
8338f124be | ||
|
|
be19f7ad4f | ||
|
|
5c737d29ab | ||
|
|
92c59ea4a5 | ||
|
|
dd28074436 | ||
|
|
f8ab30fa42 | ||
|
|
581ad8ba1e | ||
|
|
5703c1ed6d | ||
|
|
a71a3cdf3f | ||
|
|
f5d31b78e6 | ||
|
|
e1e6bb72c6 | ||
|
|
d89ca33cdb | ||
|
|
58ecfeb815 | ||
|
|
955d01a302 | ||
|
|
f8a5f7595d | ||
|
|
9f33cf19ad | ||
|
|
6deb85e083 | ||
|
|
9ad108281e | ||
|
|
cd0091c045 | ||
|
|
01ecae2c55 | ||
|
|
cef4bcd756 | ||
|
|
ad50bd7154 | ||
|
|
b97f9cf5ec | ||
|
|
2d65d3d73b | ||
|
|
733a76fdba | ||
|
|
fd63698005 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,4 @@
|
||||
presentation
|
||||
test.coffee
|
||||
parser.output
|
||||
test/fixtures/underscore
|
||||
examples/beautiful_code/parse.coffee
|
||||
*.gem
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2010 Jeremy Ashkenas
|
||||
Copyright (c) 2009 Jeremy Ashkenas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
3
README
3
README
@@ -36,6 +36,3 @@
|
||||
|
||||
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.
|
||||
|
||||
19
Rakefile
19
Rakefile
@@ -17,27 +17,18 @@ namespace :build do
|
||||
sh "racc #{args[:racc_args]} -o lib/coffee_script/parser.rb lib/coffee_script/grammar.y"
|
||||
end
|
||||
|
||||
desc "Compile the Narwhal interface"
|
||||
desc "Compile the Narwhal interface for --interactive and --run"
|
||||
task :narwhal do
|
||||
sh "bin/coffee src/narwhal/*.coffee -o lib/coffee_script/narwhal"
|
||||
end
|
||||
|
||||
desc "Continually compile the CoffeeScript/Node.js components with --watch"
|
||||
task :node do
|
||||
sh "bin/coffee -w src/*.coffee -o lib/coffee_script/"
|
||||
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 extras/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage"
|
||||
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"
|
||||
@@ -62,8 +53,6 @@ namespace :gem do
|
||||
|
||||
desc 'Build and install the coffee-script gem'
|
||||
task :install do
|
||||
verbose = "lib/coffee_script/parser.output"
|
||||
FileUtils.rm(verbose) if File.exists?(verbose)
|
||||
sh "gem build coffee-script.gemspec"
|
||||
sh "sudo gem install #{Dir['*.gem'].join(' ')} --local --no-ri --no-rdoc"
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'coffee-script'
|
||||
s.version = '0.3.2' # Keep version in sync with coffee-script.rb
|
||||
s.date = '2010-2-8'
|
||||
s.version = '0.2.0' # Keep version in sync with coffee-script.rb
|
||||
s.date = '2010-1-5'
|
||||
|
||||
s.homepage = "http://jashkenas.github.com/coffee-script/"
|
||||
s.summary = "The CoffeeScript Compiler"
|
||||
@@ -22,6 +22,5 @@ Gem::Specification.new do |s|
|
||||
s.require_paths = ['lib']
|
||||
s.executables = ['coffee']
|
||||
|
||||
s.files = Dir['bin/*', 'examples/*', 'extras/**/*', 'lib/**/*',
|
||||
'coffee-script.gemspec', 'LICENSE', 'README', 'package.json']
|
||||
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README', 'package.json']
|
||||
end
|
||||
@@ -5,5 +5,3 @@ volume: 10 if band isnt spinal_tap
|
||||
let_the_wild_rumpus_begin() unless answer is no
|
||||
|
||||
if car.speed < speed_limit then accelerate()
|
||||
|
||||
print "My name is " + @name
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
backwards: ->
|
||||
alert arguments.reverse()
|
||||
|
||||
backwards "stairway", "to", "heaven"
|
||||
@@ -4,4 +4,4 @@ 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
|
||||
roid.explode() if roid.overlaps(roid2)
|
||||
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()
|
||||
@@ -1,5 +0,0 @@
|
||||
cholesterol: 127
|
||||
|
||||
healthy: 200 > cholesterol > 60
|
||||
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
solipsism: true if mind? and not world?
|
||||
|
||||
speed ?= 140
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
solipsism: true if mind? and not world?
|
||||
@@ -1,4 +1,4 @@
|
||||
grade: (student) ->
|
||||
grade: student =>
|
||||
if student.excellent_work
|
||||
"A+"
|
||||
else if student.okay_stuff
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# The first ten global properties.
|
||||
|
||||
globals: (name for name of window)[0...10]
|
||||
globals: (name for property, name in window)[0...10]
|
||||
@@ -2,5 +2,5 @@ alert(
|
||||
try
|
||||
nonexistent / undefined
|
||||
catch error
|
||||
"And the error is ... " + error
|
||||
"The error is: " + error
|
||||
)
|
||||
@@ -1,6 +0,0 @@
|
||||
Account: (customer, cart) ->
|
||||
@customer: customer
|
||||
@cart: cart
|
||||
|
||||
$('.shopping_cart').bind 'click', (event) =>
|
||||
@customer.purchase @cart
|
||||
@@ -1,2 +1,2 @@
|
||||
square: (x) -> x * x
|
||||
cube: (x) -> square(x) * x
|
||||
square: x => x * x
|
||||
cube: x => square(x) * x
|
||||
@@ -1,5 +0,0 @@
|
||||
html: '''
|
||||
<strong>
|
||||
cup of coffeescript
|
||||
</strong>
|
||||
'''
|
||||
@@ -1,5 +0,0 @@
|
||||
weather_report: (location) ->
|
||||
# Make an Ajax request to fetch the weather...
|
||||
[location, 72, "Mostly Sunny"]
|
||||
|
||||
[city, temp, forecast]: weather_report "Berkeley, CA"
|
||||
@@ -1,4 +1,3 @@
|
||||
years_old: {max: 10, ida: 9, tim: 11}
|
||||
|
||||
ages: for child, age of years_old
|
||||
child + " is " + age
|
||||
ages: child + " is " + age for age, child in years_old
|
||||
@@ -1,13 +0,0 @@
|
||||
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
|
||||
@@ -6,7 +6,7 @@ opposite_day: true
|
||||
number: -42 if opposite_day
|
||||
|
||||
# Functions:
|
||||
square: (x) -> x * x
|
||||
square: x => x * x
|
||||
|
||||
# Arrays:
|
||||
list: [1, 2, 3, 4, 5]
|
||||
@@ -15,15 +15,15 @@ list: [1, 2, 3, 4, 5]
|
||||
math: {
|
||||
root: Math.sqrt
|
||||
square: square
|
||||
cube: (x) -> x * square x
|
||||
cube: x => x * square(x)
|
||||
}
|
||||
|
||||
# Splats:
|
||||
race: (winner, runners...) ->
|
||||
print winner, runners
|
||||
race: winner, *runners =>
|
||||
print(winner, runners)
|
||||
|
||||
# Existence:
|
||||
alert "I knew it!" if elvis?
|
||||
alert("I knew it!") if elvis?
|
||||
|
||||
# Array comprehensions:
|
||||
cubed_list: math.cube num for num in list
|
||||
cubed_list: math.cube(num) for num in list
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
bait: 1000
|
||||
and_switch: 0
|
||||
|
||||
[bait, and_switch]: [and_switch, bait]
|
||||
@@ -1,6 +1,3 @@
|
||||
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)
|
||||
for i in [0...eggs.length] by 12
|
||||
dozen_eggs: eggs[i...i+12]
|
||||
deliver(new egg_carton(dozen))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
num: 1
|
||||
change_numbers: ->
|
||||
change_numbers: =>
|
||||
new_num: -1
|
||||
num: 10
|
||||
new_num: change_numbers()
|
||||
@@ -1 +0,0 @@
|
||||
lottery.draw_winner()?.address?.zipcode
|
||||
@@ -1,6 +1,6 @@
|
||||
gold: silver: the_field: "unknown"
|
||||
|
||||
award_medals: (first, second, rest...) ->
|
||||
medalists: first, second, *rest =>
|
||||
gold: first
|
||||
silver: second
|
||||
the_field: rest
|
||||
@@ -18,8 +18,8 @@ contenders: [
|
||||
"Usain Bolt"
|
||||
]
|
||||
|
||||
award_medals contenders...
|
||||
medalists(*contenders)
|
||||
|
||||
alert "Gold: " + gold
|
||||
alert "Silver: " + silver
|
||||
alert "The Field: " + the_field
|
||||
alert("Gold: " + gold)
|
||||
alert("Silver: " + silver)
|
||||
alert("The Field: " + the_field)
|
||||
@@ -1,21 +1,21 @@
|
||||
Animal: ->
|
||||
Animal::move: (meters) ->
|
||||
alert @name + " moved " + meters + "m."
|
||||
Animal: =>
|
||||
Animal.prototype.move: meters =>
|
||||
alert(this.name + " moved " + meters + "m.")
|
||||
|
||||
Snake: (name) -> @name: name
|
||||
Snake: name => this.name: name
|
||||
Snake extends Animal
|
||||
Snake::move: ->
|
||||
alert "Slithering..."
|
||||
super 5
|
||||
Snake.prototype.move: =>
|
||||
alert("Slithering...")
|
||||
super(5)
|
||||
|
||||
Horse: (name) -> @name: name
|
||||
Horse: name => this.name: name
|
||||
Horse extends Animal
|
||||
Horse::move: ->
|
||||
alert "Galloping..."
|
||||
super 45
|
||||
Horse.prototype.move: =>
|
||||
alert("Galloping...")
|
||||
super(45)
|
||||
|
||||
sam: new Snake "Sammy the Python"
|
||||
tom: new Horse "Tommy the Palomino"
|
||||
sam: new Snake("Sammy the Python")
|
||||
tom: new Horse("Tommy the Palomino")
|
||||
|
||||
sam.move()
|
||||
tom.move()
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
switch day
|
||||
when "Mon" then go_to_work()
|
||||
when "Tue" then go_to_the_park()
|
||||
when "Thu" then go_ice_fishing()
|
||||
when "Fri", "Sat"
|
||||
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 "Sun" then go_to_church()
|
||||
when "Sunday" then go_to_church()
|
||||
else go_to_work()
|
||||
@@ -2,6 +2,6 @@ try
|
||||
all_hell_breaks_loose()
|
||||
cats_and_dogs_living_together()
|
||||
catch error
|
||||
print error
|
||||
print(error)
|
||||
finally
|
||||
clean_up()
|
||||
@@ -1,10 +1,5 @@
|
||||
# Econ 101
|
||||
if this.studying_economics
|
||||
while supply > demand then buy()
|
||||
while supply < demand then sell()
|
||||
while demand > supply
|
||||
sell()
|
||||
restock()
|
||||
|
||||
# Nursery Rhyme
|
||||
num: 6
|
||||
lyrics: while num -= 1
|
||||
num + " little monkeys, jumping on the bed.
|
||||
One fell out and bumped his head."
|
||||
while supply > demand then buy()
|
||||
@@ -6,7 +6,7 @@ body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
div.container {
|
||||
width: 950px;
|
||||
width: 850px;
|
||||
margin: 50px 0 50px 50px;
|
||||
}
|
||||
p {
|
||||
@@ -77,7 +77,7 @@ div.code {
|
||||
}
|
||||
div.code pre {
|
||||
float: left;
|
||||
width: 450px;
|
||||
width: 410px;
|
||||
border-left: 1px dotted #559;
|
||||
padding: 0 0 0 12px;
|
||||
margin: 0;
|
||||
|
||||
@@ -28,7 +28,6 @@ pre.idle .LibraryConstant {
|
||||
color: #A535AE;
|
||||
}
|
||||
pre.idle .FunctionArgument {
|
||||
color: #0076ad;
|
||||
}
|
||||
pre.idle .BuiltInConstant {
|
||||
color: #A535AE;
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
<%#
|
||||
TODO:
|
||||
Multiline and nested array comprehensions (and filters with 'when').
|
||||
Range comprehension examples (expression endpoints), with steps.
|
||||
Object comprehension examples.
|
||||
Significant Whitespace Rules.
|
||||
Newline-delimited Matrix.
|
||||
Automatic newline escaping.
|
||||
All functions are named functions.
|
||||
Splats in function definitions.
|
||||
(Multiple) splats as arguments to a function call.
|
||||
Exists?
|
||||
Array assignment splice literals.
|
||||
%>
|
||||
|
||||
<%
|
||||
require 'uv'
|
||||
def code_for(file, executable=false)
|
||||
@@ -40,8 +55,8 @@
|
||||
<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,
|
||||
plenty of holes in the lexer and leaks in the syntax. <i>There is no guarantee,
|
||||
explicit or implied, of its suitability for any purpose.</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
|
||||
@@ -51,7 +66,7 @@
|
||||
|
||||
<p>
|
||||
<b>Latest Version:</b>
|
||||
<a href="http://gemcutter.org/gems/coffee-script">0.3.2</a>
|
||||
<a href="http://gemcutter.org/gems/coffee-script">0.2.0</a>
|
||||
</p>
|
||||
|
||||
<h2>Table of Contents</h2>
|
||||
@@ -65,23 +80,21 @@
|
||||
<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="#splats">Splats</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="#existence">The Existential Operator</a><br />
|
||||
<a href="#inheritance">Inheritance, and Calling Super from a Subclass</a><br />
|
||||
<a href="#pattern_matching">Pattern Matching</a><br />
|
||||
<a href="#fat_arrow">Function Binding</a><br />
|
||||
<a href="#blocks">Blocks</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="#comparisons">Chained Comparisons</a><br />
|
||||
<a href="#strings">Multiline Strings and Heredocs</a><br />
|
||||
<a href="#strings">Multiline Strings</a><br />
|
||||
<a href="#resources">Resources</a><br />
|
||||
<a href="#contributing">Contributing</a><br />
|
||||
<a href="#change_log">Change Log</a><br />
|
||||
</p>
|
||||
|
||||
@@ -94,13 +107,9 @@
|
||||
<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
|
||||
of <a href="http://documentcloud.github.com/underscore/">Underscore.js</a>
|
||||
to CoffeeScript, which, when compiled, can pass the complete Underscore test suite.
|
||||
Or, clone the source and take a look in the
|
||||
<a href="http://github.com/jashkenas/coffee-script/tree/master/examples/">examples</a> folder.
|
||||
</p>
|
||||
|
||||
@@ -118,7 +127,7 @@ gem install coffee-script</pre>
|
||||
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://nodejs.org/">Node.js</a> (or <a href="http://narwhaljs.org/">Narwhal</a>), the <tt>coffee</tt>
|
||||
<a href="http://narwhaljs.org/">Narwhal</a>, the <tt>coffee</tt>
|
||||
command also provides direct evaluation and an interactive REPL.
|
||||
When compiling to JavaScript, <tt>coffee</tt> writes the output
|
||||
as <tt>.js</tt> files in the same directory by default, but output
|
||||
@@ -130,16 +139,14 @@ gem install coffee-script</pre>
|
||||
<td width="25%"><code>-i, --interactive</code></td>
|
||||
<td>
|
||||
Launch an interactive CoffeeScript session.
|
||||
Requires <a href="http://nodejs.org/">Node.js</a>,
|
||||
or <a href="http://narwhaljs.org/">Narwhal</a>, with <tt>--narwhal</tt>.
|
||||
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://nodejs.org/">Node.js</a>,
|
||||
or <a href="http://narwhaljs.org/">Narwhal</a>, with <tt>--narwhal</tt>.
|
||||
JavaScript. Requires <a href="http://narwhaljs.org/">Narwhal</a>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -174,7 +181,7 @@ gem install coffee-script</pre>
|
||||
<td><code>-e, --eval</code></td>
|
||||
<td>
|
||||
Compile and print a little snippet of CoffeeScript directly from the
|
||||
command line (or from <b>stdin</b>). For example:<br /><tt>coffee -e "square: (x) -> x * x"</tt>
|
||||
command line (or from <b>stdin</b>). For example:<br /><tt>coffee -e "square: x => x * x"</tt>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -188,22 +195,16 @@ gem install coffee-script</pre>
|
||||
<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 nodes in the
|
||||
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 Node.js 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.)
|
||||
Compile the JavaScript without the top-level function safety wrapper
|
||||
or var declarations, for situations where you want to add every
|
||||
variable to global scope.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -248,27 +249,13 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
use indentation.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You don't need to use parentheses to invoke a function, if you're passing
|
||||
arguments:<br /><tt>print "coffee"</tt>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can use newlines to break up your expression into smaller pieces,
|
||||
as long as CoffeeScript can determine that the line hasn't finished yet.
|
||||
</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 by default, for easier debugging.
|
||||
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>
|
||||
If you'd like to create an anonymous function, just wrap it in parentheses:
|
||||
<tt>((x) -> x * x)</tt>
|
||||
</p>
|
||||
|
||||
<p id="assignment">
|
||||
<b class="header">Assignment</b>
|
||||
@@ -278,7 +265,7 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
</p>
|
||||
<%= code_for('assignment', 'greeting') %>
|
||||
<p>
|
||||
Declaration of new variables are pushed up to the top of the nearest
|
||||
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>
|
||||
|
||||
@@ -287,7 +274,7 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
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. Feel free to mix
|
||||
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(",")') %>
|
||||
@@ -312,14 +299,9 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
If you'd like to create top-level variables for other scripts to use,
|
||||
attach them as properties on <b>window</b>, or on the <b>exports</b>
|
||||
object in CommonJS. The <b>existential operator</b> (below), gives you a
|
||||
reliable way to figure out where to add them, if you're targeting both
|
||||
CommonJS and the browser: <tt>root: exports ? this</tt>
|
||||
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">
|
||||
@@ -341,6 +323,16 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
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,
|
||||
@@ -369,37 +361,22 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
<p>
|
||||
For single-line statements, <tt>unless</tt> can be used as the inverse of <tt>if</tt>.
|
||||
</p>
|
||||
<p>
|
||||
As a shortcut for <tt>this.property</tt>, you can use <tt>@property</tt>.
|
||||
</p>
|
||||
<%= code_for('aliases') %>
|
||||
|
||||
<p id="splats">
|
||||
<b class="header">Splats...</b>
|
||||
<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 numbers of arguments a little bit more palatable.
|
||||
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 <b>while</b> loop. The
|
||||
main difference from JavaScript is that the <b>while</b> loop can be used
|
||||
as an expression, returning an array containing the result of each iteration
|
||||
through the loop.
|
||||
The only low-level loop that CoffeeScript provides is the while loop.
|
||||
</p>
|
||||
<%= code_for('while', 'lyrics.join("\n")') %>
|
||||
<%= 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
|
||||
@@ -420,14 +397,12 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
<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).
|
||||
end of your comprehension:
|
||||
</p>
|
||||
<%= code_for('range_comprehensions', 'countdown') %>
|
||||
<%= code_for('range_comprehensions') %>
|
||||
<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.
|
||||
Comprehensions can also be used to iterate over the values and keys in
|
||||
an object:
|
||||
</p>
|
||||
<%= code_for('object_comprehensions', 'ages.join(", ")') %>
|
||||
|
||||
@@ -457,11 +432,6 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
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:
|
||||
@@ -479,41 +449,6 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
into a function call:
|
||||
</p>
|
||||
<%= code_for('expressions_try', true) %>
|
||||
<p>
|
||||
There are a handful of statements in JavaScript that can't be meaningfully
|
||||
converted into expressions, namely <tt>break</tt>, <tt>continue</tt>,
|
||||
and <tt>return</tt>. If you make use of them within a block of code,
|
||||
CoffeeScript won't try to perform the conversion.
|
||||
</p>
|
||||
|
||||
<p id="existence">
|
||||
<b class="header">The Existential 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. CoffeeScript's existential 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>
|
||||
<p>
|
||||
It can also be used for safer conditional assignment than <tt>||=</tt>
|
||||
provides, for cases where you may be handling numbers or strings.
|
||||
</p>
|
||||
<%= code_for('existence', 'speed') %>
|
||||
<p>
|
||||
The accessor variant of the existential operator <tt>?.</tt> can be used to soak
|
||||
up null references in a chain of properties. Use it instead
|
||||
of the dot accessor <tt>.</tt> in cases where the base value may be <b>null</b>
|
||||
or <b>undefined</b>. If all of the properties exist then you'll get the expected
|
||||
result, if the chain is broken, <b>undefined</b> is returned instead of
|
||||
the <b>TypeError</b> that would be raised otherwise.
|
||||
</p>
|
||||
<%= code_for('soaks') %>
|
||||
<p>
|
||||
Soaking up nulls is similar to Ruby's
|
||||
<a href="http://andand.rubyforge.org/">andand gem</a>, and to the
|
||||
<a href="http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator%28%3F.%29">safe navigation operator</a>
|
||||
in Groovy.
|
||||
</p>
|
||||
|
||||
<p id="inheritance">
|
||||
<b class="header">Inheritance, and Calling Super from a Subclass</b>
|
||||
@@ -527,54 +462,26 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
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.
|
||||
set the prototype chain. CoffeeScript provides <tt>extends</tt>
|
||||
to help with prototype setup, and converts
|
||||
<tt>super()</tt> calls into calls against the immediate ancestor's
|
||||
method of the same name.
|
||||
</p>
|
||||
<%= code_for('super', true) %>
|
||||
|
||||
<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 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('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="fat_arrow">
|
||||
<b class="header">Function binding</b>
|
||||
The fat arrow <tt>=></tt> can be used to both define a function, and to bind
|
||||
it to the current value of <tt>this</tt>, right on the spot. This is helpful
|
||||
when using callback-based libraries like Prototype or jQuery, for creating
|
||||
iterator functions to pass to <tt>each</tt>, or event-handler functions
|
||||
to use with <tt>bind</tt>. Functions created with the fat arrow are able to access
|
||||
properties of the <tt>this</tt> where they're defined.
|
||||
</p>
|
||||
<%= code_for('fat_arrow') %>
|
||||
<%= code_for('blocks') %>
|
||||
|
||||
<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.
|
||||
If you ever need to interpolate literal JavaScript snippets, you can
|
||||
use backticks to pass JavaScript straight through.
|
||||
</p>
|
||||
<%= code_for('embedded', 'hi()') %>
|
||||
|
||||
@@ -588,11 +495,6 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
in a returnable, assignable expression. The format is: <tt>switch</tt> condition,
|
||||
<tt>when</tt> clauses, <tt>else</tt> the default case.
|
||||
</p>
|
||||
<p>
|
||||
As in Ruby, <b>switch</b> statements in CoffeeScript can take multiple
|
||||
values for each <b>when</b> clause. If any of the values match, the clause
|
||||
runs.
|
||||
</p>
|
||||
<%= code_for('switch') %>
|
||||
|
||||
<p id="try">
|
||||
@@ -602,158 +504,77 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
|
||||
</p>
|
||||
<%= code_for('try') %>
|
||||
|
||||
<p id="comparisons">
|
||||
<b class="header">Chained Comparisons</b>
|
||||
CoffeeScript borrows
|
||||
<a href="http://docs.python.org/reference/expressions.html#notin">chained comparisons</a>
|
||||
from Python — making it easy to test if a value falls within a
|
||||
certain range.
|
||||
</p>
|
||||
<%= code_for('comparisons', 'healthy') %>
|
||||
|
||||
<p id="strings">
|
||||
<b class="header">Multiline Strings and Heredocs</b>
|
||||
<b class="header">Multiline Strings</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 />
|
||||
Use <tt>bin/coffee</tt> to test your changes,
|
||||
<tt>rake test</tt> to run the test suite,
|
||||
<tt>rake build:parser</tt> to regenerate the Racc parser if you're
|
||||
working on the grammar,
|
||||
and <tt>rake gem:install</tt> to
|
||||
create and install a custom version of the gem.
|
||||
Use <tt>bin/coffee</tt> to test your changes, or <tt>rake gem:install</tt> to
|
||||
create and install a custom version of the gem. If you're hacking on the
|
||||
parser, use <tt>rake build:parser</tt> to rebuild it.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://github.com/jashkenas/coffee-script/issues">CoffeeScript Issues</a><br />
|
||||
Bugs reports, feature requests, and general discussion all belong here.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://github.com/mattly/rack-coffee">rack-coffee</a><br />
|
||||
Rack middleware for serving CoffeeScripts as JavaScript directly to
|
||||
the browser, without having to compile them first. From
|
||||
<a href="http://github.com/mattly">Matt Lyon</a>.
|
||||
<a href="http://github.com/jashkenas/coffee-script/issues">Bugs and Feature Requests</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://github.com/jnicklas/bistro_car">BistroCar</a><br />
|
||||
A Rails plugin by
|
||||
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 in
|
||||
CoffeeScript:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://github.com/inem/coffee-haml-filter">coffee-haml-filter</a><br />
|
||||
A custom <a href="http://haml-lang.com/">HAML</a> filter, by
|
||||
<a href="http://github.com/inem">Ivan Nemytchenko</a>, that embeds
|
||||
snippets of CoffeeScript within your HAML templates.
|
||||
A clean, safe syntax for manipulating the prototype chain, and performing
|
||||
inheritance. <a href="#inheritance"><b>extends</b> and <b>super</b></a> are the start of this, but
|
||||
aren't a complete answer.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://github.com/creationix/coffeepot">CoffeePot</a><br />
|
||||
An implementation of CoffeeScript, written in CoffeeScript, by
|
||||
<a href="http://github.com/creationix">Tim Caswell</a>. Compiles just
|
||||
a limited subset at this point.
|
||||
A CoffeeScript version of the compiler, perhaps using Alessandro Warth's
|
||||
<a href="http://tinlizzie.org/ometa/">OMeta</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.3.2</b>
|
||||
<tt>@property</tt> is now a shorthand for <tt>this.property</tt>.<br />
|
||||
Switched the default JavaScript engine from Narwhal to Node.js. Pass
|
||||
the <tt>--narwhal</tt> flag if you'd like to continue using it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.3.0</b>
|
||||
CoffeeScript 0.3 includes major syntax changes:
|
||||
<br />
|
||||
The function symbol was changed to
|
||||
<tt>-></tt>, and the bound function symbol is now <tt>=></tt>.
|
||||
<br />
|
||||
Parameter lists in function definitions must now be wrapped in parentheses.
|
||||
<br />
|
||||
Added property soaking, with the <tt>?.</tt> operator.
|
||||
<br />
|
||||
Made parentheses optional, when invoking functions with arguments.
|
||||
<br />
|
||||
Removed the obsolete block literal syntax.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.6</b>
|
||||
Added Python-style chained comparisons, the conditional existence
|
||||
operator <tt>?=</tt>, and some examples from <i>Beautiful Code</i>.
|
||||
Bugfixes relating to statement-to-expression conversion, arguments-to-array
|
||||
conversion, and the TextMate syntax highlighter.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b class="header" style="margin-top: 20px;">0.2.5</b>
|
||||
The conditions in switch statements can now take multiple values at once —
|
||||
If any of them are true, the case will run. Added the long arrow <tt>==></tt>,
|
||||
which defines and immediately binds a function to <tt>this</tt>. While loops can
|
||||
now be used as expressions, in the same way that comprehensions can. Splats
|
||||
can be used within pattern matches to soak up the rest of an array.
|
||||
</p>
|
||||
|
||||
<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 existential operator. Many thanks to all the folks who posted issues,
|
||||
with special thanks to
|
||||
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>
|
||||
|
||||
@@ -10,5 +10,4 @@
|
||||
let_the_wild_rumpus_begin();
|
||||
}
|
||||
car.speed < speed_limit ? accelerate() : null;
|
||||
print("My name is " + this.name);
|
||||
})();
|
||||
@@ -1,8 +0,0 @@
|
||||
(function(){
|
||||
var backwards;
|
||||
backwards = function backwards() {
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
return alert(arguments.reverse());
|
||||
};
|
||||
backwards("stairway", "to", "heaven");
|
||||
})();
|
||||
@@ -1,24 +1,32 @@
|
||||
(function(){
|
||||
var __a, __b, __c, __d, __e, __f, __g, food, lunch, roid, roid2;
|
||||
var __a, __b, __c, __d, __e, __f, __g, __h, __i, __j, 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));
|
||||
__a = ['toast', 'cheese', 'wine'];
|
||||
__c = [];
|
||||
for (__b in __a) {
|
||||
if (__a.hasOwnProperty(__b)) {
|
||||
food = __a[__b];
|
||||
__d = eat(food);
|
||||
__c.push(__d);
|
||||
}
|
||||
}
|
||||
return __a;
|
||||
}).call(this);
|
||||
return __c;
|
||||
})();
|
||||
// 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();
|
||||
__e = asteroids;
|
||||
for (__f in __e) {
|
||||
if (__e.hasOwnProperty(__f)) {
|
||||
roid = __e[__f];
|
||||
__h = asteroids;
|
||||
for (__i in __h) {
|
||||
if (__h.hasOwnProperty(__i)) {
|
||||
roid2 = __h[__i];
|
||||
if (roid !== roid2) {
|
||||
if (roid.overlaps(roid2)) {
|
||||
roid.explode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1,5 +0,0 @@
|
||||
(function(){
|
||||
var cholesterol, healthy;
|
||||
cholesterol = 127;
|
||||
healthy = (200 > cholesterol) && (cholesterol > 60);
|
||||
})();
|
||||
@@ -1,5 +1,5 @@
|
||||
(function(){
|
||||
var date, expensive, mood;
|
||||
var date, mood;
|
||||
if (singing) {
|
||||
mood = greatly_improved;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
(function(){
|
||||
var solipsism, speed;
|
||||
if ((typeof mind !== "undefined" && mind !== null) && (typeof !world !== "undefined" && !world !== null)) {
|
||||
var solipsism;
|
||||
if ((typeof mind !== "undefined" && mind !== null) && !(typeof world !== "undefined" && world !== null)) {
|
||||
solipsism = true;
|
||||
}
|
||||
speed = (typeof speed !== "undefined" && speed !== null) ? speed : 140;
|
||||
})();
|
||||
@@ -1,4 +1,4 @@
|
||||
(function(){
|
||||
var one, six, three, two;
|
||||
six = ((one = 1)) + ((two = 2)) + ((three = 3));
|
||||
six = (one = 1) + (two = 2) + (three = 3);
|
||||
})();
|
||||
@@ -1,14 +1,16 @@
|
||||
(function(){
|
||||
var __a, __b, globals, name;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
var __a, __b, __c, globals, name, property;
|
||||
// The first ten global properties.
|
||||
globals = ((function() {
|
||||
__a = []; __b = window;
|
||||
for (name in __b) {
|
||||
if (__hasProp.call(__b, name)) {
|
||||
__a.push(name);
|
||||
__a = window;
|
||||
__b = [];
|
||||
for (name in __a) {
|
||||
if (__a.hasOwnProperty(name)) {
|
||||
property = __a[name];
|
||||
__c = name;
|
||||
__b.push(__c);
|
||||
}
|
||||
}
|
||||
return __a;
|
||||
}).call(this)).slice(0, 10);
|
||||
return __b;
|
||||
})()).slice(0, 10);
|
||||
})();
|
||||
@@ -3,7 +3,7 @@
|
||||
try {
|
||||
return nonexistent / undefined;
|
||||
} catch (error) {
|
||||
return "And the error is ... " + error;
|
||||
return "The error is: " + error;
|
||||
}
|
||||
}).call(this));
|
||||
})());
|
||||
})();
|
||||
@@ -1,17 +0,0 @@
|
||||
(function(){
|
||||
var Account;
|
||||
Account = function Account(customer, cart) {
|
||||
var __a;
|
||||
this.customer = customer;
|
||||
this.cart = cart;
|
||||
__a = $('.shopping_cart').bind('click', (function(__this) {
|
||||
var __func = function(event) {
|
||||
return this.customer.purchase(this.cart);
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
return Account === this.constructor ? this : __a;
|
||||
};
|
||||
})();
|
||||
@@ -1,4 +0,0 @@
|
||||
(function(){
|
||||
var html;
|
||||
html = "<strong>\n cup of coffeescript\n</strong>";
|
||||
})();
|
||||
@@ -1,11 +0,0 @@
|
||||
(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];
|
||||
})();
|
||||
@@ -1,19 +1,20 @@
|
||||
(function(){
|
||||
var __a, __b, age, ages, child, years_old;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
var __a, __b, __c, 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 (__hasProp.call(__b, child)) {
|
||||
__a.push(child + " is " + age);
|
||||
__a = years_old;
|
||||
__b = [];
|
||||
for (child in __a) {
|
||||
if (__a.hasOwnProperty(child)) {
|
||||
age = __a[child];
|
||||
__c = child + " is " + age;
|
||||
__b.push(__c);
|
||||
}
|
||||
}
|
||||
return __a;
|
||||
}).call(this);
|
||||
return __b;
|
||||
})();
|
||||
})();
|
||||
@@ -1,17 +0,0 @@
|
||||
(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];
|
||||
})();
|
||||
@@ -1,5 +1,5 @@
|
||||
(function(){
|
||||
var __a, __b, __c, cubed_list, list, math, num, number, opposite_day, race, square;
|
||||
var __a, __b, __c, __d, cubed_list, list, math, num, number, opposite_day, race, square;
|
||||
// Assignment:
|
||||
number = 42;
|
||||
opposite_day = true;
|
||||
@@ -33,11 +33,15 @@
|
||||
}
|
||||
// Array comprehensions:
|
||||
cubed_list = (function() {
|
||||
__a = []; __b = list;
|
||||
for (__c = 0; __c < __b.length; __c++) {
|
||||
num = __b[__c];
|
||||
__a.push(math.cube(num));
|
||||
__a = list;
|
||||
__c = [];
|
||||
for (__b in __a) {
|
||||
if (__a.hasOwnProperty(__b)) {
|
||||
num = __a[__b];
|
||||
__d = math.cube(num);
|
||||
__c.push(__d);
|
||||
}
|
||||
}
|
||||
return __a;
|
||||
}).call(this);
|
||||
return __c;
|
||||
})();
|
||||
})();
|
||||
@@ -1,8 +0,0 @@
|
||||
(function(){
|
||||
var __a, and_switch, bait;
|
||||
bait = 1000;
|
||||
and_switch = 0;
|
||||
__a = [and_switch, bait];
|
||||
bait = __a[0];
|
||||
and_switch = __a[1];
|
||||
})();
|
||||
@@ -1,21 +1,9 @@
|
||||
(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;
|
||||
}).call(this);
|
||||
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));
|
||||
}).call(this));
|
||||
}
|
||||
return __f;
|
||||
};
|
||||
var __a, __b, __c, __d, __e, dozen_eggs, i;
|
||||
__d = 0;
|
||||
__e = eggs.length;
|
||||
for (__c=0, i=__d; (__d <= __e ? i < __e : i > __e); (__d <= __e ? i += 12 : i -= 12), __c++) {
|
||||
dozen_eggs = eggs.slice(i, i + 12);
|
||||
deliver(new egg_carton(dozen));
|
||||
}
|
||||
})();
|
||||
@@ -1,4 +0,0 @@
|
||||
(function(){
|
||||
var __a;
|
||||
((__a = lottery.draw_winner()) == undefined ? undefined : __a.address == undefined ? undefined : __a.address.zipcode);
|
||||
})();
|
||||
@@ -1,7 +1,7 @@
|
||||
(function(){
|
||||
var award_medals, contenders, gold, silver, the_field;
|
||||
gold = (silver = (the_field = "unknown"));
|
||||
award_medals = function award_medals(first, second) {
|
||||
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;
|
||||
@@ -9,7 +9,7 @@
|
||||
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"];
|
||||
award_medals.apply(this, contenders);
|
||||
medalists.apply(this, contenders);
|
||||
alert("Gold: " + gold);
|
||||
alert("Silver: " + silver);
|
||||
alert("The Field: " + the_field);
|
||||
|
||||
@@ -1,32 +1,25 @@
|
||||
(function(){
|
||||
var Animal, Horse, Snake, __a, __b, sam, tom;
|
||||
Animal = function Animal() { };
|
||||
var Animal, Horse, Snake, 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;
|
||||
return this.name = name;
|
||||
};
|
||||
__a = function(){};
|
||||
__a.prototype = Animal.prototype;
|
||||
Snake.__superClass__ = Animal.prototype;
|
||||
Snake.prototype = new __a();
|
||||
Snake.prototype = new Animal();
|
||||
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;
|
||||
return this.name = name;
|
||||
};
|
||||
__b = function(){};
|
||||
__b.prototype = Animal.prototype;
|
||||
Horse.__superClass__ = Animal.prototype;
|
||||
Horse.prototype = new __b();
|
||||
Horse.prototype = new Animal();
|
||||
Horse.prototype.constructor = Horse;
|
||||
Horse.prototype.move = function move() {
|
||||
alert("Galloping...");
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
(function(){
|
||||
if (day === "Mon") {
|
||||
go_to_work();
|
||||
} else if (day === "Tue") {
|
||||
if (day === "Tuesday") {
|
||||
eat_breakfast();
|
||||
} else if (day === "Wednesday") {
|
||||
go_to_the_park();
|
||||
} else if (day === "Thu") {
|
||||
go_ice_fishing();
|
||||
} else if (day === "Fri" || day === "Sat") {
|
||||
} else if (day === "Saturday") {
|
||||
if (day === bingo_day) {
|
||||
go_to_bingo();
|
||||
go_dancing();
|
||||
}
|
||||
} else if (day === "Sun") {
|
||||
} else if (day === "Sunday") {
|
||||
go_to_church();
|
||||
} else {
|
||||
go_to_work();
|
||||
|
||||
@@ -1,22 +1,9 @@
|
||||
(function(){
|
||||
var __a, lyrics, num;
|
||||
// Econ 101
|
||||
if (this.studying_economics) {
|
||||
while (supply > demand) {
|
||||
buy();
|
||||
}
|
||||
while (supply < demand) {
|
||||
sell();
|
||||
}
|
||||
while (demand > supply) {
|
||||
sell();
|
||||
restock();
|
||||
}
|
||||
while (supply > demand) {
|
||||
buy();
|
||||
}
|
||||
// Nursery Rhyme
|
||||
num = 6;
|
||||
lyrics = (function() {
|
||||
__a = [];
|
||||
while (num -= 1) {
|
||||
__a.push(num + " little monkeys, jumping on the bed. \
|
||||
One fell out and bumped his head.");
|
||||
}
|
||||
return __a;
|
||||
}).call(this);
|
||||
})();
|
||||
@@ -1,53 +0,0 @@
|
||||
<!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);
|
||||
|
||||
var f1 = function f1() {
|
||||
return arr;
|
||||
};
|
||||
|
||||
JSLitmus.test('regular function', function() {
|
||||
f1();
|
||||
});
|
||||
|
||||
var __this = this;
|
||||
|
||||
var f2 = function f2() {
|
||||
return (function() {
|
||||
return arr;
|
||||
}).apply(__this, arguments);
|
||||
};
|
||||
|
||||
JSLitmus.test('bound function', function() {
|
||||
f2();
|
||||
});
|
||||
|
||||
var f3 = (function() {
|
||||
__b = function() {
|
||||
return arr;
|
||||
};
|
||||
return (function f2() {
|
||||
return __b.apply(__this, arguments);
|
||||
});
|
||||
})();
|
||||
|
||||
JSLitmus.test('prebound function', function() {
|
||||
f3();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -14,12 +14,13 @@
|
||||
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) 2010 Jeremy Ashkenas, DocumentCloud Inc.</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>
|
||||
@@ -30,56 +31,56 @@
|
||||
<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"> 14 </span> root<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"> 18 </span> previousUnderscore<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="FunctionName">wrapper</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</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"> 24 </span> <span class="FunctionName">wrapper</span><span class="Keyword">:</span> <span class="FunctionArgument">obj</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 25 </span> <span class="Variable">this</span>._wrapped<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"> 30 </span> breaker<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="FunctionName">_</span><span class="Keyword">:</span> <span class="FunctionName">root._</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> <span class="Keyword">new</span> <span class="TypeName">wrapper</span>(obj)
|
||||
<span class="line-numbers"> 33 </span> <span class="Comment"><span class="Comment">#</span> Create a safe reference to the Underscore object for reference below.</span>
|
||||
<span class="line-numbers"> 34 </span> _<span class="Keyword">:</span> <span class="FunctionName">root._</span><span class="Keyword">:</span> <span class="FunctionArgument">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"> 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> exports._<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> <span class="FunctionName">Array:</span><span class="Keyword">:</span>slice
|
||||
<span class="line-numbers"> 43 </span> <span class="FunctionName">unshift</span><span class="Keyword">:</span> <span class="FunctionName">Array:</span><span class="Keyword">:</span>unshift
|
||||
<span class="line-numbers"> 44 </span> <span class="FunctionName">toString</span><span class="Keyword">:</span> <span class="FunctionName">Object:</span><span class="Keyword">:</span>toString
|
||||
<span class="line-numbers"> 45 </span> <span class="FunctionName">hasOwnProperty</span><span class="Keyword">:</span> <span class="FunctionName">Object:</span><span class="Keyword">:</span>hasOwnProperty
|
||||
<span class="line-numbers"> 46 </span> <span class="FunctionName">propertyIsEnumerable</span><span class="Keyword">:</span> <span class="FunctionName">Object:</span><span class="Keyword">:</span>propertyIsEnumerable
|
||||
<span class="line-numbers"> 42 </span> slice<span class="Keyword">:</span> Array.prototype.slice
|
||||
<span class="line-numbers"> 43 </span> unshift<span class="Keyword">:</span> Array.prototype.unshift
|
||||
<span class="line-numbers"> 44 </span> toString<span class="Keyword">:</span> Object.prototype.toString
|
||||
<span class="line-numbers"> 45 </span> hasOwnProperty<span class="Keyword">:</span> Object.prototype.hasOwnProperty
|
||||
<span class="line-numbers"> 46 </span> propertyIsEnumerable<span class="Keyword">:</span> Object.prototype.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.7<span class="String">'</span></span>
|
||||
<span class="line-numbers"> 50 </span> _.VERSION<span class="Keyword">:</span> <span class="String"><span class="String">'</span>0.5.3<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="FunctionName">_.each</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</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"> 57 </span> <span class="FunctionName">_.each</span><span class="Keyword">:</span> <span class="FunctionArgument">obj, iterator, context</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 58 </span> index<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"> 63 </span> iterator.call(context, val, key, obj) <span class="Keyword">for</span> val, key <span class="Keyword">in</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
|
||||
@@ -87,145 +88,145 @@
|
||||
<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="FunctionName">_.map</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 71 </span> <span class="FunctionName">_.map</span><span class="Keyword">:</span> <span class="FunctionArgument">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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 73 </span> results<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="FunctionName">_.reduce</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, memo, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 81 </span> <span class="FunctionName">_.reduce</span><span class="Keyword">:</span> <span class="FunctionArgument">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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</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"> 83 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 84 </span> memo<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="FunctionName">_.reduceRight</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, memo, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 90 </span> <span class="FunctionName">_.reduceRight</span><span class="Keyword">:</span> <span class="FunctionArgument">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">(</span><span class="FunctionArgument">value, index</span><span class="FunctionArgument">)</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"> 92 </span> _.each(_.clone(_.toArray(obj)).reverse())<span class="FunctionArgument"> value, index </span><span class="Storage">=></span>
|
||||
<span class="line-numbers"> 93 </span> memo<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="FunctionName">_.detect</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 98 </span> <span class="FunctionName">_.detect</span><span class="Keyword">:</span> <span class="FunctionArgument">obj, iterator, context</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 99 </span> result<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"> 102 </span> result<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="FunctionName">_.select</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 109 </span> <span class="FunctionName">_.select</span><span class="Keyword">:</span> <span class="FunctionArgument">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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 111 </span> results<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="FunctionName">_.reject</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 118 </span> <span class="FunctionName">_.reject</span><span class="Keyword">:</span> <span class="FunctionArgument">obj, iterator, context</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 119 </span> results<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="FunctionName">_.all</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 127 </span> <span class="FunctionName">_.all</span><span class="Keyword">:</span> <span class="FunctionArgument">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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</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"> 130 </span> result<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> (result<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="FunctionName">_.any</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 138 </span> <span class="FunctionName">_.any</span><span class="Keyword">:</span> <span class="FunctionArgument">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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</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"> 141 </span> result<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> (result<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="FunctionName">_.include</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, target</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 149 </span> <span class="FunctionName">_.include</span><span class="Keyword">:</span> <span class="FunctionArgument">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"> 151 </span> <span class="Keyword">for</span> val <span class="Keyword">in</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="FunctionName">_.invoke</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, method</span><span class="FunctionArgument">)</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"> 157 </span> <span class="FunctionName">_.invoke</span><span class="Keyword">:</span> <span class="FunctionArgument">obj, method</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 158 </span> args<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="FunctionName">_.pluck</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, key</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 164 </span> _.map(obj, (<span class="FunctionArgument">(</span><span class="FunctionArgument">val</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> val[key]))
|
||||
<span class="line-numbers"> 163 </span> <span class="FunctionName">_.pluck</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.max</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 168 </span> <span class="FunctionName">_.max</span><span class="Keyword">:</span> <span class="FunctionArgument">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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</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"> 170 </span> result<span class="Keyword">:</span> {computed<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> computed<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> (result<span class="Keyword">:</span> {value<span class="Keyword">:</span> value, computed<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="FunctionName">_.min</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 178 </span> <span class="FunctionName">_.min</span><span class="Keyword">:</span> <span class="FunctionArgument">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">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</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"> 180 </span> result<span class="Keyword">:</span> {computed<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> computed<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> (result<span class="Keyword">:</span> {value<span class="Keyword">:</span> value, computed<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="FunctionName">_.sortBy</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, iterator, context</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 189 </span> _.pluck(((_.map obj, <span class="FunctionArgument">(</span><span class="FunctionArgument">value, index, list</span><span class="FunctionArgument">)</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">(</span><span class="FunctionArgument">left, right</span><span class="FunctionArgument">)</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"> 188 </span> <span class="FunctionName">_.sortBy</span><span class="Keyword">:</span> <span class="FunctionArgument">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> {value<span class="Keyword">:</span> value, criteria<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> a<span class="Keyword">:</span> left.criteria; b<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"> 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="FunctionName">_.sortedIndex</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, obj, iterator</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 199 </span> <span class="FunctionName">_.sortedIndex</span><span class="Keyword">:</span> <span class="FunctionArgument">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"> 201 </span> low<span class="Keyword">:</span> <span class="Number">0</span>; high<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"> 203 </span> mid<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> low<span class="Keyword">:</span> mid <span class="Keyword">+</span> <span class="Number">1</span> <span class="Keyword">else</span> high<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="FunctionName">_.toArray</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">iterable</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 209 </span> <span class="FunctionName">_.toArray</span><span class="Keyword">:</span> <span class="FunctionArgument">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))
|
||||
@@ -234,7 +235,7 @@
|
||||
<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="FunctionName">_.size</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> _.toArray(obj).length
|
||||
<span class="line-numbers"> 218 </span> <span class="FunctionName">_.size</span><span class="Keyword">:</span> <span class="FunctionArgument">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>
|
||||
@@ -242,7 +243,7 @@
|
||||
<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="FunctionName">_.first</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, n, guard</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 226 </span> <span class="FunctionName">_.first</span><span class="Keyword">:</span> <span class="FunctionArgument">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>
|
||||
@@ -250,36 +251,36 @@
|
||||
<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="FunctionName">_.rest</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, index, guard</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 234 </span> <span class="FunctionName">_.rest</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.last</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> array[array.length <span class="Keyword">-</span> <span class="Number">1</span>]
|
||||
<span class="line-numbers"> 239 </span> <span class="FunctionName">_.last</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.compact</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</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"> 243 </span> <span class="FunctionName">_.compact</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.flatten</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 248 </span> _.reduce array, [], <span class="FunctionArgument">(</span><span class="FunctionArgument">memo, value</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 247 </span> <span class="FunctionName">_.flatten</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.without</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</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"> 255 </span> <span class="FunctionName">_.without</span><span class="Keyword">:</span> <span class="FunctionArgument">array</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 256 </span> values<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="FunctionName">_.uniq</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, isSorted</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 263 </span> <span class="FunctionName">memo</span><span class="Keyword">:</span> []
|
||||
<span class="line-numbers"> 262 </span> <span class="FunctionName">_.uniq</span><span class="Keyword">:</span> <span class="FunctionArgument">array, isSorted</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 263 </span> memo<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
|
||||
@@ -287,350 +288,329 @@
|
||||
<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="FunctionName">_.intersect</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array</span><span class="FunctionArgument">)</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">(</span><span class="FunctionArgument">item</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 274 </span> _.all rest, <span class="FunctionArgument">(</span><span class="FunctionArgument">other</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 271 </span> <span class="FunctionName">_.intersect</span><span class="Keyword">:</span> <span class="FunctionArgument">array</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 272 </span> rest<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="FunctionName">_.zip</span><span class="Keyword">:</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 281 </span> <span class="FunctionName">length</span><span class="Keyword">:</span> _.max(_.pluck(arguments, <span class="String"><span class="String">'</span>length<span class="String">'</span></span>))
|
||||
<span class="line-numbers"> 282 </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"> 283 </span> <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">0</span>...length]
|
||||
<span class="line-numbers"> 284 </span> results[i]<span class="Keyword">:</span> _.pluck(arguments, String(i))
|
||||
<span class="line-numbers"> 285 </span> results
|
||||
<span class="line-numbers"> 286 </span>
|
||||
<span class="line-numbers"> 280 </span> <span class="FunctionName">_.zip</span><span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 281 </span> args<span class="Keyword">:</span> _.toArray(arguments)
|
||||
<span class="line-numbers"> 282 </span> length<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> results<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="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"> 289 </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"> 290 </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"> 291 </span> <span class="FunctionName">_.indexOf</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, item</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 292 </span> <span class="Keyword">return</span> array.indexOf(item) <span class="Keyword">if</span> array.indexOf
|
||||
<span class="line-numbers"> 293 </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"> 294 </span> <span class="Keyword">while</span> l <span class="Keyword">-</span> i
|
||||
<span class="line-numbers"> 295 </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"> 296 </span> <span class="Keyword">-</span><span class="Number">1</span>
|
||||
<span class="line-numbers"> 297 </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="FunctionName">_.indexOf</span><span class="Keyword">:</span> <span class="FunctionArgument">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> i<span class="Keyword">:</span> <span class="Number">0</span>; l<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="Comment"><span class="Comment">#</span> Provide JavaScript 1.6's lastIndexOf, delegating to the native function,</span>
|
||||
<span class="line-numbers"> 300 </span> <span class="Comment"><span class="Comment">#</span> if possible.</span>
|
||||
<span class="line-numbers"> 301 </span> <span class="FunctionName">_.lastIndexOf</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">array, item</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 302 </span> <span class="Keyword">return</span> array.lastIndexOf(item) <span class="Keyword">if</span> array.lastIndexOf
|
||||
<span class="line-numbers"> 303 </span> <span class="FunctionName">i</span><span class="Keyword">:</span> array.length
|
||||
<span class="line-numbers"> 304 </span> <span class="Keyword">while</span> i
|
||||
<span class="line-numbers"> 305 </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"> 306 </span> <span class="Keyword">-</span><span class="Number">1</span>
|
||||
<span class="line-numbers"> 307 </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="FunctionName">_.lastIndexOf</span><span class="Keyword">:</span> <span class="FunctionArgument">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> i<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="Comment"><span class="Comment">#</span> Generate an integer Array containing an arithmetic progression. A port of</span>
|
||||
<span class="line-numbers"> 310 </span> <span class="Comment"><span class="Comment">#</span> the native Python range() function. See:</span>
|
||||
<span class="line-numbers"> 311 </span> <span class="Comment"><span class="Comment">#</span> http://docs.python.org/library/functions.html#range</span>
|
||||
<span class="line-numbers"> 312 </span> <span class="FunctionName">_.range</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">start, stop, step</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 313 </span> <span class="FunctionName">a</span><span class="Keyword">:</span> arguments
|
||||
<span class="line-numbers"> 314 </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"> 315 </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"> 316 </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"> 317 </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"> 318 </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"> 319 </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"> 320 </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"> 321 </span> <span class="FunctionName">idx</span><span class="Keyword">:</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 322 </span> <span class="Keyword">while</span> <span class="BuiltInConstant">true</span>
|
||||
<span class="line-numbers"> 323 </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"> 324 </span> range[idx]<span class="Keyword">:</span> i
|
||||
<span class="line-numbers"> 325 </span> idx<span class="Keyword">++</span>
|
||||
<span class="line-numbers"> 326 </span> i<span class="Keyword">+</span><span class="Keyword">=</span> step
|
||||
<span class="line-numbers"> 327 </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="FunctionName">_.range</span><span class="Keyword">:</span> <span class="FunctionArgument">start, stop, step</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 314 </span> a<span class="Keyword">:</span> _.toArray(arguments)
|
||||
<span class="line-numbers"> 315 </span> solo<span class="Keyword">:</span> a.length <span class="Keyword"><=</span> <span class="Number">1</span>
|
||||
<span class="line-numbers"> 316 </span> i<span class="Keyword">:</span> start<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> stop<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> step<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> len<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> range<span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Array</span>(len)
|
||||
<span class="line-numbers"> 322 </span> idx<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="Comment"><span class="Comment">#</span> ----------------------- Function Functions: -----------------------------</span>
|
||||
<span class="line-numbers"> 330 </span>
|
||||
<span class="line-numbers"> 331 </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"> 332 </span> <span class="Comment"><span class="Comment">#</span> optionally). Binding with arguments is also known as 'curry'.</span>
|
||||
<span class="line-numbers"> 333 </span> <span class="FunctionName">_.bind</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">func, obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 334 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
|
||||
<span class="line-numbers"> 335 </span> <span class="Storage">-></span> func.apply(obj <span class="Keyword">or</span> root, args.concat(arguments))
|
||||
<span class="line-numbers"> 336 </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="FunctionName">_.bind</span><span class="Keyword">:</span> <span class="FunctionArgument">func, obj</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 335 </span> args<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="Comment"><span class="Comment">#</span> Bind all of an object's methods to that object. Useful for ensuring that</span>
|
||||
<span class="line-numbers"> 339 </span> <span class="Comment"><span class="Comment">#</span> all callbacks defined on an object belong to it.</span>
|
||||
<span class="line-numbers"> 340 </span> <span class="FunctionName">_.bindAll</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 341 </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"> 342 </span> _.each(funcs, <span class="FunctionArgument">(</span><span class="FunctionArgument">f</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> obj[f]<span class="Keyword">:</span> _.bind(obj[f], obj))
|
||||
<span class="line-numbers"> 343 </span> obj
|
||||
<span class="line-numbers"> 344 </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="FunctionName">_.bindAll</span><span class="Keyword">:</span> <span class="FunctionArgument">obj</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 342 </span> funcs<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="Comment"><span class="Comment">#</span> Delays a function for the given number of milliseconds, and then calls</span>
|
||||
<span class="line-numbers"> 347 </span> <span class="Comment"><span class="Comment">#</span> it with the arguments supplied.</span>
|
||||
<span class="line-numbers"> 348 </span> <span class="FunctionName">_.delay</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">func, wait</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 349 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> _.rest(arguments, <span class="Number">2</span>)
|
||||
<span class="line-numbers"> 350 </span> setTimeout((<span class="Storage">-></span> func.apply(func, args)), wait)
|
||||
<span class="line-numbers"> 351 </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="FunctionName">_.delay</span><span class="Keyword">:</span> <span class="FunctionArgument">func, wait</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 350 </span> args<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="Comment"><span class="Comment">#</span> Defers a function, scheduling it to run after the current call stack has</span>
|
||||
<span class="line-numbers"> 354 </span> <span class="Comment"><span class="Comment">#</span> cleared.</span>
|
||||
<span class="line-numbers"> 355 </span> <span class="FunctionName">_.defer</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">func</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 356 </span> _.delay.apply(_, [func, <span class="Number">1</span>].concat(_.rest(arguments)))
|
||||
<span class="line-numbers"> 357 </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="FunctionName">_.defer</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Returns the first function passed as an argument to the second,</span>
|
||||
<span class="line-numbers"> 360 </span> <span class="Comment"><span class="Comment">#</span> allowing you to adjust arguments, run code before and after, and</span>
|
||||
<span class="line-numbers"> 361 </span> <span class="Comment"><span class="Comment">#</span> conditionally execute the original function.</span>
|
||||
<span class="line-numbers"> 362 </span> <span class="FunctionName">_.wrap</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">func, wrapper</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 363 </span> <span class="Storage">-></span> wrapper.apply(wrapper, [func].concat(arguments))
|
||||
<span class="line-numbers"> 364 </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="FunctionName">_.wrap</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Returns a function that is the composition of a list of functions, each</span>
|
||||
<span class="line-numbers"> 367 </span> <span class="Comment"><span class="Comment">#</span> consuming the return value of the function that follows.</span>
|
||||
<span class="line-numbers"> 368 </span> <span class="FunctionName">_.compose</span><span class="Keyword">:</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 369 </span> <span class="FunctionName">funcs</span><span class="Keyword">:</span> arguments
|
||||
<span class="line-numbers"> 370 </span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 371 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> arguments
|
||||
<span class="line-numbers"> 372 </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"> 373 </span> <span class="FunctionName">args</span><span class="Keyword">:</span> [funcs[i].apply(<span class="Variable">this</span>, args)]
|
||||
<span class="line-numbers"> 374 </span> args[<span class="Number">0</span>]
|
||||
<span class="line-numbers"> 375 </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="FunctionName">_.compose</span><span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 370 </span> funcs<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> args<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> args<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="Comment"><span class="Comment">#</span> ------------------------- Object Functions: ----------------------------</span>
|
||||
<span class="line-numbers"> 378 </span>
|
||||
<span class="line-numbers"> 379 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the names of an object's properties.</span>
|
||||
<span class="line-numbers"> 380 </span> <span class="FunctionName">_.keys</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 381 </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"> 382 </span> key <span class="Keyword">for</span> key, val <span class="Keyword">of</span> obj
|
||||
<span class="line-numbers"> 383 </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="FunctionName">_.keys</span><span class="Keyword">:</span> <span class="FunctionArgument">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> val, key <span class="Keyword">in</span> obj
|
||||
<span class="line-numbers"> 384 </span>
|
||||
<span class="line-numbers"> 385 </span> <span class="Comment"><span class="Comment">#</span> Retrieve the values of an object's properties.</span>
|
||||
<span class="line-numbers"> 386 </span> <span class="FunctionName">_.values</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 387 </span> _.map(obj, _.identity)
|
||||
<span class="line-numbers"> 388 </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="FunctionName">_.values</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Return a sorted list of the function names available in Underscore.</span>
|
||||
<span class="line-numbers"> 391 </span> <span class="FunctionName">_.functions</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 392 </span> _.select(_.keys(obj), <span class="FunctionArgument">(</span><span class="FunctionArgument">key</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> _.isFunction(obj[key])).sort()
|
||||
<span class="line-numbers"> 393 </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="FunctionName">_.functions</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Extend a given object with all of the properties in a source object.</span>
|
||||
<span class="line-numbers"> 396 </span> <span class="FunctionName">_.extend</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">destination, source</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 397 </span> <span class="Keyword">for</span> key, val <span class="Keyword">of</span> source
|
||||
<span class="line-numbers"> 398 </span> destination[key]<span class="Keyword">:</span> val
|
||||
<span class="line-numbers"> 399 </span> destination
|
||||
<span class="line-numbers"> 400 </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="FunctionName">_.extend</span><span class="Keyword">:</span> <span class="FunctionArgument">destination, source</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 398 </span> <span class="Keyword">for</span> val, key <span class="Keyword">in</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="Comment"><span class="Comment">#</span> Create a (shallow-cloned) duplicate of an object.</span>
|
||||
<span class="line-numbers"> 403 </span> <span class="FunctionName">_.clone</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 404 </span> <span class="Keyword">return</span> obj.slice(<span class="Number">0</span>) <span class="Keyword">if</span> _.isArray(obj)
|
||||
<span class="line-numbers"> 405 </span> _.extend({}, obj)
|
||||
<span class="line-numbers"> 406 </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="FunctionName">_.clone</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Invokes interceptor with the obj, and then returns obj.</span>
|
||||
<span class="line-numbers"> 409 </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"> 410 </span> <span class="FunctionName">_.tap</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, interceptor</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 411 </span> interceptor(obj)
|
||||
<span class="line-numbers"> 412 </span> obj
|
||||
<span class="line-numbers"> 413 </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="FunctionName">_.tap</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Perform a deep comparison to check if two objects are equal.</span>
|
||||
<span class="line-numbers"> 416 </span> <span class="FunctionName">_.isEqual</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">a, b</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 417 </span> <span class="Comment"><span class="Comment">#</span> Check object identity.</span>
|
||||
<span class="line-numbers"> 418 </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"> 419 </span> <span class="Comment"><span class="Comment">#</span> Different types?</span>
|
||||
<span class="line-numbers"> 420 </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"> 421 </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"> 422 </span> <span class="Comment"><span class="Comment">#</span> Basic equality test (watch out for coercions).</span>
|
||||
<span class="line-numbers"> 423 </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"> 424 </span> <span class="Comment"><span class="Comment">#</span> One is falsy and the other truthy.</span>
|
||||
<span class="line-numbers"> 425 </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"> 426 </span> <span class="Comment"><span class="Comment">#</span> One of them implements an isEqual()?</span>
|
||||
<span class="line-numbers"> 427 </span> <span class="Keyword">return</span> a.isEqual(b) <span class="Keyword">if</span> a.isEqual
|
||||
<span class="line-numbers"> 428 </span> <span class="Comment"><span class="Comment">#</span> Check dates' integer values.</span>
|
||||
<span class="line-numbers"> 429 </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"> 430 </span> <span class="Comment"><span class="Comment">#</span> Both are NaN?</span>
|
||||
<span class="line-numbers"> 431 </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"> 432 </span> <span class="Comment"><span class="Comment">#</span> Compare regular expressions.</span>
|
||||
<span class="line-numbers"> 433 </span> <span class="Keyword">if</span> _.isRegExp(a) <span class="Keyword">and</span> _.isRegExp(b)
|
||||
<span class="line-numbers"> 434 </span> <span class="Keyword">return</span> a.source <span class="Keyword">is</span> b.source <span class="Keyword">and</span>
|
||||
<span class="line-numbers"> 435 </span> a.global <span class="Keyword">is</span> b.global <span class="Keyword">and</span>
|
||||
<span class="line-numbers"> 436 </span> a.ignoreCase <span class="Keyword">is</span> b.ignoreCase <span class="Keyword">and</span>
|
||||
<span class="line-numbers"> 437 </span> a.multiline <span class="Keyword">is</span> b.multiline
|
||||
<span class="line-numbers"> 438 </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"> 439 </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"> 440 </span> <span class="Comment"><span class="Comment">#</span> Check for different array lengths before comparing contents.</span>
|
||||
<span class="line-numbers"> 441 </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"> 442 </span> <span class="Comment"><span class="Comment">#</span> Nothing else worked, deep compare the contents.</span>
|
||||
<span class="line-numbers"> 443 </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"> 444 </span> <span class="Comment"><span class="Comment">#</span> Different object sizes?</span>
|
||||
<span class="line-numbers"> 445 </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"> 446 </span> <span class="Comment"><span class="Comment">#</span> Recursive comparison of contents.</span>
|
||||
<span class="line-numbers"> 447 </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"> 448 </span> <span class="Keyword">return</span> <span class="BuiltInConstant">true</span>
|
||||
<span class="line-numbers"> 449 </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="FunctionName">_.isEqual</span><span class="Keyword">:</span> <span class="FunctionArgument">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> atype<span class="Keyword">:</span> <span class="Keyword">typeof</span>(a); btype<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> aKeys<span class="Keyword">:</span> _.keys(a); bKeys<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="Comment"><span class="Comment">#</span> Is a given array or object empty?</span>
|
||||
<span class="line-numbers"> 452 </span> <span class="FunctionName">_.isEmpty</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> _.keys(obj).length <span class="Keyword">is</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 453 </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="FunctionName">_.isEmpty</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Is a given value a DOM element?</span>
|
||||
<span class="line-numbers"> 456 </span> <span class="FunctionName">_.isElement</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</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"> 457 </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="FunctionName">_.isElement</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Is a given value an array?</span>
|
||||
<span class="line-numbers"> 460 </span> <span class="FunctionName">_.isArray</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</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"> 461 </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="FunctionName">_.isArray</span><span class="Keyword">:</span> <span class="FunctionArgument">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="Comment"><span class="Comment">#</span> Is a given variable an arguments object?</span>
|
||||
<span class="line-numbers"> 464 </span> <span class="FunctionName">_.isArguments</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> obj <span class="Keyword">and</span> _.isNumber(obj.length) <span class="Keyword">and</span> <span class="Keyword">not</span> obj.concat <span class="Keyword">and</span>
|
||||
<span class="line-numbers"> 465 </span> <span class="Keyword">not</span> obj.substr <span class="Keyword">and</span> <span class="Keyword">not</span> obj.apply <span class="Keyword">and</span> <span class="Keyword">not</span> propertyIsEnumerable.call(obj, <span class="String"><span class="String">'</span>length<span class="String">'</span></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="FunctionName">_.isArguments</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.isFunction</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</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"> 469 </span> <span class="FunctionName">_.isFunction</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.isString</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</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"> 473 </span> <span class="FunctionName">_.isString</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.isNumber</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> (obj <span class="Keyword">is</span> <span class="Keyword">+</span>obj) <span class="Keyword">or</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"> 477 </span> <span class="FunctionName">_.isNumber</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.isDate</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</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"> 481 </span> <span class="FunctionName">_.isDate</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.isRegExp</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</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"> 485 </span> <span class="FunctionName">_.isRegExp</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.isNaN</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> _.isNumber(obj) <span class="Keyword">and</span> window.isNaN(obj)
|
||||
<span class="line-numbers"> 490 </span> <span class="FunctionName">_.isNaN</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.isNull</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> obj <span class="Keyword">is</span> <span class="BuiltInConstant">null</span>
|
||||
<span class="line-numbers"> 494 </span> <span class="FunctionName">_.isNull</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.isUndefined</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj</span><span class="FunctionArgument">)</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"> 498 </span> <span class="FunctionName">_.isUndefined</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.noConflict</span><span class="Keyword">:</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"> 505 </span> <span class="FunctionName">_.noConflict</span><span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 506 </span> root._<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="FunctionName">_.identity</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">value</span><span class="FunctionArgument">)</span> <span class="Storage">-></span> value
|
||||
<span class="line-numbers"> 511 </span> <span class="FunctionName">_.identity</span><span class="Keyword">:</span> <span class="FunctionArgument">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="FunctionName">_.breakLoop</span><span class="Keyword">:</span> <span class="Storage">-></span> <span class="Keyword">throw</span> breaker
|
||||
<span class="line-numbers"> 515 </span> <span class="FunctionName">_.breakLoop</span><span class="Keyword">:</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="FunctionName">_.uniqueId</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">prefix</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 520 </span> idCounter<span class="Keyword">:</span> <span class="Number">0</span>
|
||||
<span class="line-numbers"> 521 </span> <span class="FunctionName">_.uniqueId</span><span class="Keyword">:</span> <span class="FunctionArgument">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> By default, Underscore uses ERB-style template delimiters, change the</span>
|
||||
<span class="line-numbers"> 526 </span> <span class="Comment"><span class="Comment">#</span> following template settings to use alternative delimiters.</span>
|
||||
<span class="line-numbers"> 527 </span> <span class="FunctionName">_.templateSettings</span><span class="Keyword">:</span> {
|
||||
<span class="line-numbers"> 528 </span> <span class="FunctionName">start</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span><%<span class="String">'</span></span>
|
||||
<span class="line-numbers"> 529 </span> <span class="FunctionName">end</span><span class="Keyword">:</span> <span class="String"><span class="String">'</span>%><span class="String">'</span></span>
|
||||
<span class="line-numbers"> 530 </span> <span class="FunctionName">interpolate</span><span class="Keyword">:</span><span class="String"> <span class="String">/</span><%=(.+?)%><span class="String">/</span>g</span>
|
||||
<span class="line-numbers"> 531 </span> }
|
||||
<span class="line-numbers"> 532 </span>
|
||||
<span class="line-numbers"> 533 </span>
|
||||
<span class="line-numbers"> 534 </span> <span class="Comment"><span class="Comment">#</span> JavaScript templating a-la ERB, pilfered from John Resig's</span>
|
||||
<span class="line-numbers"> 535 </span> <span class="Comment"><span class="Comment">#</span> "Secrets of the JavaScript Ninja", page 83.</span>
|
||||
<span class="line-numbers"> 536 </span> <span class="Comment"><span class="Comment">#</span> Single-quotea fix from Rick Strahl's version.</span>
|
||||
<span class="line-numbers"> 537 </span> <span class="FunctionName">_.template</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">str, data</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 538 </span> <span class="FunctionName">c</span><span class="Keyword">:</span> _.templateSettings
|
||||
<span class="line-numbers"> 539 </span> <span class="FunctionName">fn</span><span class="Keyword">:</span> <span class="Keyword">new</span> <span class="TypeName">Function</span> <span class="String"><span class="String">'</span>obj<span class="String">'</span></span>,
|
||||
<span class="line-numbers"> 540 </span> <span class="String"><span class="String">'</span>var p=[],print=function(){p.push.apply(p,arguments);};<span class="String">'</span></span> <span class="Keyword">+</span>
|
||||
<span class="line-numbers"> 541 </span> <span class="String"><span class="String">'</span>with(obj){p.push(<span class="UserDefinedConstant">\'</span><span class="String">'</span></span> <span class="Keyword">+</span>
|
||||
<span class="line-numbers"> 542 </span> str.replace(<span class="String"><span class="String">/</span>[<span class="UserDefinedConstant">\r</span><span class="UserDefinedConstant">\t</span><span class="UserDefinedConstant">\n</span>]<span class="String">/</span>g</span>, <span class="String"><span class="String">"</span> <span class="String">"</span></span>)
|
||||
<span class="line-numbers"> 543 </span> .replace(<span class="Keyword">new</span> <span class="TypeName">RegExp</span>(<span class="String"><span class="String">"</span>'(?=[^<span class="String">"</span></span><span class="Keyword">+</span>c.end[<span class="Number">0</span>]<span class="Keyword">+</span><span class="String"><span class="String">"</span>]*<span class="String">"</span></span><span class="Keyword">+</span>c.end<span class="Keyword">+</span><span class="String"><span class="String">"</span>)<span class="String">"</span></span>,<span class="String"><span class="String">"</span>g<span class="String">"</span></span>),<span class="String"><span class="String">"</span><span class="UserDefinedConstant">\t</span><span class="String">"</span></span>)
|
||||
<span class="line-numbers"> 544 </span> .split(<span class="String"><span class="String">"</span>'<span class="String">"</span></span>).join(<span class="String"><span class="String">"</span><span class="UserDefinedConstant">\\</span>'<span class="String">"</span></span>)
|
||||
<span class="line-numbers"> 545 </span> .split(<span class="String"><span class="String">"</span><span class="UserDefinedConstant">\t</span><span class="String">"</span></span>).join(<span class="String"><span class="String">"</span>'<span class="String">"</span></span>)
|
||||
<span class="line-numbers"> 546 </span> .replace(c.interpolate, <span class="String"><span class="String">"</span>',$1,'<span class="String">"</span></span>)
|
||||
<span class="line-numbers"> 547 </span> .split(c.start).join(<span class="String"><span class="String">"</span>');<span class="String">"</span></span>)
|
||||
<span class="line-numbers"> 548 </span> .split(c.end).join(<span class="String"><span class="String">"</span>p.push('<span class="String">"</span></span>) <span class="Keyword">+</span>
|
||||
<span class="line-numbers"> 549 </span> <span class="String"><span class="String">"</span>');}return p.join('');<span class="String">"</span></span>
|
||||
<span class="line-numbers"> 550 </span> <span class="Keyword">if</span> data <span class="Keyword">then</span> fn(data) <span class="Keyword">else</span> fn
|
||||
<span class="line-numbers"> 551 </span>
|
||||
<span class="line-numbers"> 552 </span>
|
||||
<span class="line-numbers"> 553 </span> <span class="Comment"><span class="Comment">#</span> ------------------------------- Aliases ----------------------------------</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="FunctionName">_.template</span><span class="Keyword">:</span> <span class="FunctionArgument">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> _.forEach<span class="Keyword">:</span> _.each
|
||||
<span class="line-numbers"> 546 </span> _.foldl<span class="Keyword">:</span> _.inject<span class="Keyword">:</span> _.reduce
|
||||
<span class="line-numbers"> 547 </span> _.foldr<span class="Keyword">:</span> _.reduceRight
|
||||
<span class="line-numbers"> 548 </span> _.filter<span class="Keyword">:</span> _.select
|
||||
<span class="line-numbers"> 549 </span> _.every<span class="Keyword">:</span> _.all
|
||||
<span class="line-numbers"> 550 </span> _.some<span class="Keyword">:</span> _.any
|
||||
<span class="line-numbers"> 551 </span> _.head<span class="Keyword">:</span> _.first
|
||||
<span class="line-numbers"> 552 </span> _.tail<span class="Keyword">:</span> _.rest
|
||||
<span class="line-numbers"> 553 </span> _.methods<span class="Keyword">:</span> _.functions
|
||||
<span class="line-numbers"> 554 </span>
|
||||
<span class="line-numbers"> 555 </span> <span class="FunctionName">_.forEach</span><span class="Keyword">:</span> _.each
|
||||
<span class="line-numbers"> 556 </span> <span class="FunctionName">_.foldl</span><span class="Keyword">:</span> <span class="FunctionName">_.inject</span><span class="Keyword">:</span> _.reduce
|
||||
<span class="line-numbers"> 557 </span> <span class="FunctionName">_.foldr</span><span class="Keyword">:</span> _.reduceRight
|
||||
<span class="line-numbers"> 558 </span> <span class="FunctionName">_.filter</span><span class="Keyword">:</span> _.select
|
||||
<span class="line-numbers"> 559 </span> <span class="FunctionName">_.every</span><span class="Keyword">:</span> _.all
|
||||
<span class="line-numbers"> 560 </span> <span class="FunctionName">_.some</span><span class="Keyword">:</span> _.any
|
||||
<span class="line-numbers"> 561 </span> <span class="FunctionName">_.head</span><span class="Keyword">:</span> _.first
|
||||
<span class="line-numbers"> 562 </span> <span class="FunctionName">_.tail</span><span class="Keyword">:</span> _.rest
|
||||
<span class="line-numbers"> 563 </span> <span class="FunctionName">_.methods</span><span class="Keyword">:</span> _.functions
|
||||
<span class="line-numbers"> 564 </span>
|
||||
<span class="line-numbers"> 565 </span>
|
||||
<span class="line-numbers"> 566 </span> <span class="Comment"><span class="Comment">#</span> /*------------------------ Setup the OOP Wrapper: --------------------------*/</span>
|
||||
<span class="line-numbers"> 567 </span>
|
||||
<span class="line-numbers"> 568 </span> <span class="Comment"><span class="Comment">#</span> Helper function to continue chaining intermediate results.</span>
|
||||
<span class="line-numbers"> 569 </span> <span class="FunctionName">result</span><span class="Keyword">:</span> <span class="FunctionArgument">(</span><span class="FunctionArgument">obj, chain</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 570 </span> <span class="Keyword">if</span> chain <span class="Keyword">then</span> _(obj).chain() <span class="Keyword">else</span> obj
|
||||
<span class="line-numbers"> 571 </span>
|
||||
<span class="line-numbers"> 572 </span>
|
||||
<span class="line-numbers"> 573 </span> <span class="Comment"><span class="Comment">#</span> Add all of the Underscore functions to the wrapper object.</span>
|
||||
<span class="line-numbers"> 574 </span> _.each _.functions(_), <span class="FunctionArgument">(</span><span class="FunctionArgument">name</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 575 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> _[name]
|
||||
<span class="line-numbers"> 576 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 577 </span> unshift.call(arguments, <span class="Variable">this</span>._wrapped)
|
||||
<span class="line-numbers"> 578 </span> result(method.apply(_, arguments), <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 579 </span>
|
||||
<span class="line-numbers"> 580 </span>
|
||||
<span class="line-numbers"> 581 </span> <span class="Comment"><span class="Comment">#</span> Add all mutator Array functions to the wrapper.</span>
|
||||
<span class="line-numbers"> 582 </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">(</span><span class="FunctionArgument">name</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 583 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
|
||||
<span class="line-numbers"> 584 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 585 </span> method.apply(<span class="Variable">this</span>._wrapped, arguments)
|
||||
<span class="line-numbers"> 586 </span> result(<span class="Variable">this</span>._wrapped, <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 587 </span>
|
||||
<span class="line-numbers"> 588 </span>
|
||||
<span class="line-numbers"> 589 </span> <span class="Comment"><span class="Comment">#</span> Add all accessor Array functions to the wrapper.</span>
|
||||
<span class="line-numbers"> 590 </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">(</span><span class="FunctionArgument">name</span><span class="FunctionArgument">)</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 591 </span> <span class="FunctionName">method</span><span class="Keyword">:</span> Array.prototype[name]
|
||||
<span class="line-numbers"> 592 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 593 </span> result(method.apply(<span class="Variable">this</span>._wrapped, arguments), <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 594 </span>
|
||||
<span class="line-numbers"> 595 </span>
|
||||
<span class="line-numbers"> 596 </span> <span class="Comment"><span class="Comment">#</span> Start chaining a wrapped Underscore object.</span>
|
||||
<span class="line-numbers"> 597 </span> <span class="FunctionName">wrapper::chain</span><span class="Keyword">:</span> <span class="Storage">-></span>
|
||||
<span class="line-numbers"> 598 </span> <span class="FunctionName">this._chain</span><span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
|
||||
<span class="line-numbers"> 599 </span> <span class="Variable">this</span>
|
||||
<span class="line-numbers"> 600 </span>
|
||||
<span class="line-numbers"> 601 </span>
|
||||
<span class="line-numbers"> 602 </span> <span class="Comment"><span class="Comment">#</span> Extracts the result from a wrapped and chained object.</span>
|
||||
<span class="line-numbers"> 603 </span> <span class="FunctionName">wrapper::value</span><span class="Keyword">:</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>
|
||||
<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="FunctionName">result</span><span class="Keyword">:</span> <span class="FunctionArgument">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> method<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> unshift.call(arguments, <span class="Variable">this</span>._wrapped)
|
||||
<span class="line-numbers"> 568 </span> result(method.apply(_, arguments), <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 569 </span>
|
||||
<span class="line-numbers"> 570 </span>
|
||||
<span class="line-numbers"> 571 </span> <span class="Comment"><span class="Comment">#</span> Add all mutator Array functions to the wrapper.</span>
|
||||
<span class="line-numbers"> 572 </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"> 573 </span> method<span class="Keyword">:</span> Array.prototype[name]
|
||||
<span class="line-numbers"> 574 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 575 </span> method.apply(<span class="Variable">this</span>._wrapped, arguments)
|
||||
<span class="line-numbers"> 576 </span> result(<span class="Variable">this</span>._wrapped, <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 577 </span>
|
||||
<span class="line-numbers"> 578 </span>
|
||||
<span class="line-numbers"> 579 </span> <span class="Comment"><span class="Comment">#</span> Add all accessor Array functions to the wrapper.</span>
|
||||
<span class="line-numbers"> 580 </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"> 581 </span> method<span class="Keyword">:</span> Array.prototype[name]
|
||||
<span class="line-numbers"> 582 </span> wrapper.prototype[name]<span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 583 </span> result(method.apply(<span class="Variable">this</span>._wrapped, arguments), <span class="Variable">this</span>._chain)
|
||||
<span class="line-numbers"> 584 </span>
|
||||
<span class="line-numbers"> 585 </span>
|
||||
<span class="line-numbers"> 586 </span> <span class="Comment"><span class="Comment">#</span> Start chaining a wrapped Underscore object.</span>
|
||||
<span class="line-numbers"> 587 </span> <span class="FunctionName">wrapper.prototype.chain</span><span class="Keyword">:</span> <span class="Storage">=></span>
|
||||
<span class="line-numbers"> 588 </span> <span class="Variable">this</span>._chain<span class="Keyword">:</span> <span class="BuiltInConstant">true</span>
|
||||
<span class="line-numbers"> 589 </span> <span class="Variable">this</span>
|
||||
<span class="line-numbers"> 590 </span>
|
||||
<span class="line-numbers"> 591 </span>
|
||||
<span class="line-numbers"> 592 </span> <span class="Comment"><span class="Comment">#</span> Extracts the result from a wrapped and chained object.</span>
|
||||
<span class="line-numbers"> 593 </span> <span class="FunctionName">wrapper.prototype.value</span><span class="Keyword">:</span> <span class="Storage">=></span> <span class="Variable">this</span>._wrapped
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# Beautiful Code, Chapter 6.
|
||||
# The implementation of binary search that is tested.
|
||||
|
||||
# Return the index of an element in a sorted list. (or -1, if not present)
|
||||
index: (list, target) ->
|
||||
[low, high]: [0, list.length]
|
||||
while low < high
|
||||
mid: (low + high) >> 1
|
||||
val: list[mid]
|
||||
return mid if val is target
|
||||
if val < target then low: mid + 1 else high: mid
|
||||
return -1
|
||||
|
||||
puts 2 is index([10, 20, 30, 40, 50], 30)
|
||||
puts 4 is index([-97, 35, 67, 88, 1200], 1200)
|
||||
puts 0 is index([0, 45, 70], 0)
|
||||
@@ -1,13 +0,0 @@
|
||||
# Beautiful Code, Chapter 3.
|
||||
# Produces the expected runtime of Quicksort, for every integer from 1 to N.
|
||||
|
||||
runtime: (N) ->
|
||||
[sum, t]: [0, 0]
|
||||
for n in [1..N]
|
||||
sum += 2 * t
|
||||
t: n - 1 + sum / n
|
||||
t
|
||||
|
||||
puts runtime(3) is 2.6666666666666665
|
||||
puts runtime(5) is 7.4
|
||||
puts runtime(8) is 16.92142857142857
|
||||
@@ -1,34 +0,0 @@
|
||||
# Beautiful Code, Chapter 1.
|
||||
# Implements a regular expression matcher that supports character matches,
|
||||
# '.', '^', '$', and '*'.
|
||||
|
||||
# Search for the regexp anywhere in the text.
|
||||
match: (regexp, text) ->
|
||||
return match_here(regexp.slice(1), text) if regexp[0] is '^'
|
||||
while text
|
||||
return true if match_here(regexp, text)
|
||||
text: text.slice(1)
|
||||
false
|
||||
|
||||
# Search for the regexp at the beginning of the text.
|
||||
match_here: (regexp, text) ->
|
||||
[cur, next]: [regexp[0], regexp[1]]
|
||||
if regexp.length is 0 then return true
|
||||
if next is '*' then return match_star(cur, regexp.slice(2), text)
|
||||
if cur is '$' and not next then return text.length is 0
|
||||
if text and (cur is '.' or cur is text[0]) then return match_here(regexp.slice(1), text.slice(1))
|
||||
false
|
||||
|
||||
# Search for a kleene star match at the beginning of the text.
|
||||
match_star: (c, regexp, text) ->
|
||||
while true
|
||||
return true if match_here(regexp, text)
|
||||
return false unless text and (text[0] is c or c is '.')
|
||||
text: text.slice(1)
|
||||
|
||||
puts match("ex", "some text")
|
||||
puts match("s..t", "spit")
|
||||
puts match("^..t", "buttercup")
|
||||
puts match("i..$", "cherries")
|
||||
puts match("o*m", "vrooooommm!")
|
||||
puts match("^hel*o$", "hellllllo")
|
||||
@@ -1,57 +0,0 @@
|
||||
# After wycats' http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/
|
||||
|
||||
# Sinatra.
|
||||
get '/hello', ->
|
||||
'Hello World'
|
||||
|
||||
|
||||
# Append.
|
||||
append: (location, data) ->
|
||||
path: new Pathname location
|
||||
throw "Location does not exist" unless path.exists()
|
||||
|
||||
File.open path, 'a', (file) ->
|
||||
file.puts YAML.dump data
|
||||
|
||||
data
|
||||
|
||||
|
||||
# Rubinius' File.open implementation.
|
||||
File.open: (path, mode, block) ->
|
||||
io: new File path, mode
|
||||
|
||||
return io unless block
|
||||
|
||||
try
|
||||
block io
|
||||
finally
|
||||
try
|
||||
io.close() unless io.closed()
|
||||
catch error
|
||||
# nothing, just swallow them.
|
||||
|
||||
|
||||
# Write.
|
||||
write: (location, data) ->
|
||||
path = new Pathname location
|
||||
raise "Location does not exist" unless path.exists()
|
||||
|
||||
File.open path, 'w', (file) ->
|
||||
return false if Digest.MD5.hexdigest(file.read()) is data.hash()
|
||||
file.puts YAML.dump data
|
||||
true
|
||||
|
||||
|
||||
# Rails' respond_to.
|
||||
index: ->
|
||||
people: Person.find 'all'
|
||||
|
||||
respond_to (format) ->
|
||||
format.html()
|
||||
format.xml -> render { xml: people.xml() }
|
||||
|
||||
|
||||
# Synchronization.
|
||||
synchronize: (block) ->
|
||||
lock()
|
||||
try block() finally unlock()
|
||||
@@ -1,14 +1,14 @@
|
||||
# Functions:
|
||||
square: (x) -> x * x
|
||||
square: x => x * x
|
||||
|
||||
sum: (x, y) -> x + y
|
||||
sum: x, y => x + y
|
||||
|
||||
odd: (x) -> x % 2 isnt 0
|
||||
odd: x => x % 2 is 0
|
||||
|
||||
even: (x) -> x % 2 is 0
|
||||
even: x => x % 2 isnt 0
|
||||
|
||||
run_loop: ->
|
||||
fire_events((e) -> e.stopPropagation())
|
||||
run_loop: =>
|
||||
fire_events(e => e.stopPropagation())
|
||||
listen()
|
||||
wait()
|
||||
|
||||
@@ -22,14 +22,14 @@ spaced_out_multiline_object: {
|
||||
three: new Idea()
|
||||
|
||||
inner_obj: {
|
||||
freedom: -> _.freedom()
|
||||
freedom: => _.freedom()
|
||||
}
|
||||
}
|
||||
|
||||
# Arrays:
|
||||
stooges: [{moe: 45}, {curly: 43}, {larry: 46}]
|
||||
|
||||
exponents: [(x) -> x, (x) -> x * x, (x) -> x * x * x]
|
||||
exponents: [(x => x), (x => x * x), (x => x * x * x)]
|
||||
|
||||
empty: []
|
||||
|
||||
@@ -54,7 +54,7 @@ decoration: medal_of_honor if war_hero
|
||||
go_to_sleep() unless coffee
|
||||
|
||||
# Returning early:
|
||||
race: ->
|
||||
race: =>
|
||||
run()
|
||||
walk()
|
||||
crawl()
|
||||
@@ -103,7 +103,7 @@ while true
|
||||
|
||||
# Lexical scoping.
|
||||
v_1: 5
|
||||
change_a_and_set_b: ->
|
||||
change_a_and_set_b: =>
|
||||
v_1: 10
|
||||
v_2: 15
|
||||
v_2: 20
|
||||
@@ -128,7 +128,7 @@ activity: switch day
|
||||
else go_to_work()
|
||||
|
||||
# Semicolons can optionally be used instead of newlines.
|
||||
wednesday: -> eat_breakfast(); go_to_work(); eat_dinner()
|
||||
wednesday: => eat_breakfast(); go_to_work(); eat_dinner()
|
||||
|
||||
# Array slice literals.
|
||||
zero_to_nine: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
@@ -140,19 +140,19 @@ 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) ->
|
||||
Animal: =>
|
||||
Animal.prototype.move: meters =>
|
||||
alert(this.name + " moved " + meters + "m.")
|
||||
|
||||
Snake: (name) -> this.name: name
|
||||
Snake: name => this.name: name
|
||||
Snake extends Animal
|
||||
Snake::move: ->
|
||||
Snake.prototype.move: =>
|
||||
alert('Slithering...')
|
||||
super(5)
|
||||
|
||||
Horse: (name) -> this.name: name
|
||||
Horse: name => this.name: name
|
||||
Horse extends Animal
|
||||
Horse::move: ->
|
||||
Horse.prototype.move: =>
|
||||
alert('Galloping...')
|
||||
super(45)
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
Ported from Nicholas Zakas' collection of computer science fundamentals, written
|
||||
in JavaScript. Originals available here:
|
||||
|
||||
http://github.com/nzakas/computer-science-in-javascript
|
||||
@@ -1,25 +0,0 @@
|
||||
# Uses a binary search algorithm to locate a value in the specified array.
|
||||
binary_search: (items, value) ->
|
||||
|
||||
start: 0
|
||||
stop: items.length - 1
|
||||
pivot: Math.floor((start + stop) / 2)
|
||||
|
||||
while items[pivot] isnt value and start < stop
|
||||
|
||||
# Adjust the search area.
|
||||
stop: pivot - 1 if value < items[pivot]
|
||||
start: pivot + 1 if value > items[pivot]
|
||||
|
||||
# Recalculate the pivot.
|
||||
pivot: Math.floor((stop + start) / 2)
|
||||
|
||||
# Make sure we've found the correct value.
|
||||
if items[pivot] is value then pivot else -1
|
||||
|
||||
|
||||
# Test the function.
|
||||
puts(2 is binary_search([10, 20, 30, 40, 50], 30))
|
||||
puts(4 is binary_search([-97, 35, 67, 88, 1200], 1200))
|
||||
puts(0 is binary_search([0, 45, 70], 0))
|
||||
puts(-1 is binary_search([0, 45, 70], 10))
|
||||
@@ -1,11 +0,0 @@
|
||||
# A bubble sort implementation, sorting the given array in-place.
|
||||
bubble_sort: (list) ->
|
||||
for i in [0...list.length]
|
||||
for j in [0...list.length - i]
|
||||
[list[j], list[j+1]]: [list[j+1], list[j]] if list[j] > list[j+1]
|
||||
list
|
||||
|
||||
|
||||
# Test the function.
|
||||
puts(bubble_sort([3, 2, 1]).join(' ') is '1 2 3')
|
||||
puts(bubble_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9')
|
||||
@@ -1,106 +0,0 @@
|
||||
# "Classic" linked list implementation that doesn't keep track of its size.
|
||||
LinkedList: ->
|
||||
this._head: null # Pointer to the first item in the list.
|
||||
|
||||
|
||||
# Appends some data to the end of the list. This method traverses the existing
|
||||
# list and places the value at the end in a new node.
|
||||
LinkedList::add: (data) ->
|
||||
|
||||
# Create a new node object to wrap the data.
|
||||
node: {data: data, next: null}
|
||||
|
||||
current: this._head ||= node
|
||||
|
||||
if this._head isnt node
|
||||
current: current.next while current.next
|
||||
current.next: node
|
||||
|
||||
this
|
||||
|
||||
|
||||
# Retrieves the data at the given position in the list.
|
||||
LinkedList::item: (index) ->
|
||||
|
||||
# Check for out-of-bounds values.
|
||||
return null if index < 0
|
||||
|
||||
current: this._head or null
|
||||
i: -1
|
||||
|
||||
# Advance through the list.
|
||||
current: current.next while current and index > (i += 1)
|
||||
|
||||
# Return null if we've reached the end.
|
||||
current and current.data
|
||||
|
||||
|
||||
# Remove the item from the given location in the list.
|
||||
LinkedList::remove: (index) ->
|
||||
|
||||
# Check for out-of-bounds values.
|
||||
return null if index < 0
|
||||
|
||||
current: this._head or null
|
||||
i: -1
|
||||
|
||||
# Special case: removing the first item.
|
||||
if index is 0
|
||||
this._head: current.next
|
||||
else
|
||||
|
||||
# Find the right location.
|
||||
[previous, current]: [current, current.next] while index > (i += 1)
|
||||
|
||||
# Skip over the item to remove.
|
||||
previous.next: current.next
|
||||
|
||||
# Return the value.
|
||||
current and current.data
|
||||
|
||||
|
||||
# Calculate the number of items in the list.
|
||||
LinkedList::size: ->
|
||||
current: this._head
|
||||
count: 0
|
||||
|
||||
while current
|
||||
count += 1
|
||||
current: current.next
|
||||
|
||||
count
|
||||
|
||||
|
||||
# Convert the list into an array.
|
||||
LinkedList::toArray: ->
|
||||
result: []
|
||||
current: this._head
|
||||
|
||||
while current
|
||||
result.push(current.data)
|
||||
current: current.next
|
||||
|
||||
result
|
||||
|
||||
|
||||
# The string representation of the linked list.
|
||||
LinkedList::toString: -> this.toArray().toString()
|
||||
|
||||
|
||||
# Tests.
|
||||
list: new LinkedList()
|
||||
|
||||
list.add("Hi")
|
||||
puts(list.size() is 1)
|
||||
puts(list.item(0) is "Hi")
|
||||
puts(list.item(1) is null)
|
||||
|
||||
list: new LinkedList()
|
||||
list.add("zero").add("one").add("two")
|
||||
puts(list.size() is 3)
|
||||
puts(list.item(2) is "two")
|
||||
puts(list.remove(1) is "one")
|
||||
puts(list.item(0) is "zero")
|
||||
puts(list.item(1) is "two")
|
||||
puts(list.size() is 2)
|
||||
puts(list.item(-10) is null)
|
||||
@@ -1,36 +0,0 @@
|
||||
# Use the Luhn algorithm to validate a numeric identifier, such as credit card
|
||||
# numbers, national insurance numbers, etc.
|
||||
# See: http://en.wikipedia.org/wiki/Luhn_algorithm
|
||||
|
||||
is_valid_identifier: (identifier) ->
|
||||
|
||||
sum: 0
|
||||
alt: false
|
||||
|
||||
for i in [(identifier.length - 1)..0]
|
||||
|
||||
# Get the next digit.
|
||||
num: parseInt(identifier.charAt(i), 10)
|
||||
|
||||
# If it's not a valid number, abort.
|
||||
return false if isNaN(num)
|
||||
|
||||
# If it's an alternate number...
|
||||
if alt
|
||||
num *= 2
|
||||
num: (num % 10) + 1 if num > 9
|
||||
|
||||
# Flip the alternate bit.
|
||||
alt: !alt
|
||||
|
||||
# Add to the rest of the sum.
|
||||
sum += num
|
||||
|
||||
# Determine if it's valid.
|
||||
sum % 10 is 0
|
||||
|
||||
|
||||
# Tests.
|
||||
puts(is_valid_identifier("49927398716") is true)
|
||||
puts(is_valid_identifier("4408041234567893") is true)
|
||||
puts(is_valid_identifier("4408041234567890") is false)
|
||||
@@ -1,19 +0,0 @@
|
||||
# Sorts an array in ascending natural order using merge sort.
|
||||
merge_sort: (list) ->
|
||||
|
||||
return list if list.length is 1
|
||||
|
||||
result: []
|
||||
pivot: Math.floor(list.length / 2)
|
||||
left: merge_sort(list.slice(0, pivot))
|
||||
right: merge_sort(list.slice(pivot))
|
||||
|
||||
while left.length and right.length
|
||||
result.push(if left[0] < right[0] then left.shift() else right.shift())
|
||||
|
||||
result.concat(left).concat(right)
|
||||
|
||||
|
||||
# Test the function.
|
||||
puts(merge_sort([3, 2, 1]).join(' ') is '1 2 3')
|
||||
puts(merge_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9')
|
||||
@@ -1,23 +0,0 @@
|
||||
# An in-place selection sort.
|
||||
selection_sort: (list) ->
|
||||
len: list.length
|
||||
|
||||
# For each item in the list.
|
||||
for i in [0...len]
|
||||
|
||||
# Set the minimum to this position.
|
||||
min: i
|
||||
|
||||
# Check the rest of the array to see if anything is smaller.
|
||||
(min: j if list[j] < list[min]) for j in [(i+1)...len]
|
||||
|
||||
# Swap if a smaller item has been found.
|
||||
[list[i], list[min]]: [list[min], list[i]] if i isnt min
|
||||
|
||||
# The list is now sorted.
|
||||
list
|
||||
|
||||
|
||||
# Test the function.
|
||||
puts(selection_sort([3, 2, 1]).join(' ') is '1 2 3')
|
||||
puts(selection_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9')
|
||||
72
examples/documents.coffee
Normal file
72
examples/documents.coffee
Normal file
@@ -0,0 +1,72 @@
|
||||
# Document Model
|
||||
dc.model.Document: dc.Model.extend({
|
||||
|
||||
constructor: attributes => this.base(attributes)
|
||||
|
||||
# For display, show either the highlighted search results, or the summary,
|
||||
# if no highlights are available.
|
||||
# The import process will take care of this in the future, but the inline
|
||||
# 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, ' ')
|
||||
|
||||
# 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)))
|
||||
|
||||
bookmark: pageNumber =>
|
||||
bookmark: new dc.model.Bookmark({title: this.get('title'), page_number: pageNumber, document_id: this.id})
|
||||
Bookmarks.create(bookmark)
|
||||
|
||||
# Inspect.
|
||||
toString: => 'Document ' + this.id + ' "' + this.get('title') + '"'
|
||||
|
||||
})
|
||||
|
||||
# Document Set
|
||||
dc.model.DocumentSet: dc.model.RESTfulSet.extend({
|
||||
|
||||
resource: 'documents'
|
||||
|
||||
SELECTION_CHANGED: 'documents:selection_changed'
|
||||
|
||||
constructor: options =>
|
||||
this.base(options)
|
||||
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText')
|
||||
|
||||
selected: => _.select(this.models(), m => m.get('selected'))
|
||||
|
||||
selectedIds: => _.pluck(this.selected(), 'id')
|
||||
|
||||
countSelected: => this.selected().length
|
||||
|
||||
downloadSelectedViewers: =>
|
||||
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')
|
||||
|
||||
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')
|
||||
|
||||
# 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))
|
||||
|
||||
})
|
||||
|
||||
# The main set of Documents, used by the search tab.
|
||||
window.Documents: new dc.model.DocumentSet()
|
||||
|
||||
# The set of documents that is used to look at a particular label.
|
||||
dc.app.LabeledDocuments: new dc.model.DocumentSet()
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# ['toast', 'cheese', 'wine'].each { |food| print food.capitalize }
|
||||
|
||||
['toast', 'wine', 'cheese'].each (food) -> print(food.capitalize())
|
||||
['toast', 'wine', 'cheese'].each(food => print(food.capitalize()))
|
||||
|
||||
|
||||
|
||||
@@ -14,43 +14,10 @@
|
||||
# end
|
||||
|
||||
LotteryTicket: {
|
||||
get_picks: -> this.picks
|
||||
set_picks: (nums) -> this.picks: nums
|
||||
get_purchase: -> this.purchase
|
||||
set_purchase: (amount) -> this.purchase: amount
|
||||
}
|
||||
|
||||
|
||||
|
||||
# class << LotteryDraw
|
||||
# def play
|
||||
# result = LotteryTicket.new_random
|
||||
# winners = {}
|
||||
# @@tickets.each do |buyer, ticket_list|
|
||||
# ticket_list.each do |ticket|
|
||||
# score = ticket.score( result )
|
||||
# next if score.zero?
|
||||
# winners[buyer] ||= []
|
||||
# winners[buyer] << [ ticket, score ]
|
||||
# end
|
||||
# end
|
||||
# @@tickets.clear
|
||||
# winners
|
||||
# end
|
||||
# end
|
||||
|
||||
LotteryDraw: {
|
||||
play: ->
|
||||
result: LotteryTicket.new_random()
|
||||
winners: {}
|
||||
this.tickets.each (buyer, ticket_list) ->
|
||||
ticket_list.each (ticket) ->
|
||||
score: ticket.score(result)
|
||||
return if score is 0
|
||||
winners[buyer] ||= []
|
||||
winners[buyer].push([ticket, score])
|
||||
this.tickets: {}
|
||||
winners
|
||||
get_picks: => this.picks
|
||||
set_picks: nums => this.picks: nums
|
||||
get_purchase: => this.purchase
|
||||
set_purchase: amount => this.purchase: amount
|
||||
}
|
||||
|
||||
|
||||
@@ -65,8 +32,8 @@ LotteryDraw: {
|
||||
# end
|
||||
|
||||
WishScanner: {
|
||||
scan_for_a_wish: ->
|
||||
wish: this.read().detect((thought) -> thought.index('wish: ') is 0)
|
||||
scan_for_a_wish: =>
|
||||
wish: this.read().detect(thought => thought.index('wish: ') is 0)
|
||||
wish.replace('wish: ', '')
|
||||
}
|
||||
|
||||
@@ -111,7 +78,7 @@ WishScanner: {
|
||||
Creature : {
|
||||
|
||||
# This method applies a hit taken during a fight.
|
||||
hit: (damage) ->
|
||||
hit: damage =>
|
||||
p_up: Math.rand(this.charisma)
|
||||
if p_up % 9 is 7
|
||||
this.life += p_up / 4
|
||||
@@ -120,7 +87,7 @@ Creature : {
|
||||
if this.life <= 0 then puts("[" + this.name + " has died.]")
|
||||
|
||||
# This method takes one turn in a fight.
|
||||
fight: (enemy, weapon) ->
|
||||
fight: enemy, weapon =>
|
||||
if this.life <= 0 then return puts("[" + this.name + "is too dead to fight!]")
|
||||
|
||||
# Attack the opponent.
|
||||
@@ -156,12 +123,12 @@ Creature : {
|
||||
# Get evil idea and swap in code words
|
||||
print("Enter your new idea: ")
|
||||
idea: gets()
|
||||
code_words.each((real, code) -> idea.replace(real, code))
|
||||
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))
|
||||
File.open("idea-" + idea_name + '.txt', 'w', file => file.write(idea))
|
||||
|
||||
|
||||
|
||||
@@ -177,7 +144,7 @@ File.open("idea-" + idea_name + '.txt', 'w', (file) -> file.write(idea))
|
||||
# end
|
||||
# end
|
||||
|
||||
wipe_mutterings_from: (sentence) ->
|
||||
wipe_mutterings_from: sentence =>
|
||||
throw new Error("cannot wipe mutterings") unless sentence.indexOf
|
||||
while sentence.indexOf('(') >= 0
|
||||
open: sentence.indexOf('(') - 1
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
# 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]
|
||||
# }
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
# Underscore.coffee
|
||||
# (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
# (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.
|
||||
@@ -21,7 +21,7 @@
|
||||
# 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) ->
|
||||
wrapper: obj =>
|
||||
this._wrapped: obj
|
||||
this
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
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)
|
||||
# Create a safe reference to the Underscore object for reference below.
|
||||
_: root._: obj => new wrapper(obj)
|
||||
|
||||
|
||||
# Export the Underscore object for CommonJS.
|
||||
@@ -39,28 +39,28 @@
|
||||
|
||||
|
||||
# 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
|
||||
slice: Array.prototype.slice
|
||||
unshift: Array.prototype.unshift
|
||||
toString: Object.prototype.toString
|
||||
hasOwnProperty: Object.prototype.hasOwnProperty
|
||||
propertyIsEnumerable: Object.prototype.propertyIsEnumerable
|
||||
|
||||
|
||||
# Current version.
|
||||
_.VERSION: '0.5.7'
|
||||
_.VERSION: '0.5.3'
|
||||
|
||||
|
||||
# ------------------------ Collection Functions: ---------------------------
|
||||
|
||||
# The cornerstone, an each implementation.
|
||||
# Handles objects implementing forEach, arrays, and raw objects.
|
||||
_.each: (obj, iterator, context) ->
|
||||
_.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
|
||||
iterator.call(context, val, key, obj) for val, key in obj
|
||||
catch e
|
||||
throw e if e isnt breaker
|
||||
obj
|
||||
@@ -68,36 +68,36 @@
|
||||
|
||||
# Return the results of applying the iterator to each element. Use JavaScript
|
||||
# 1.6's version of map, if possible.
|
||||
_.map: (obj, iterator, context) ->
|
||||
_.map: obj, iterator, context =>
|
||||
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
|
||||
results: []
|
||||
_.each obj, (value, index, list) ->
|
||||
_.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) ->
|
||||
_.reduce: obj, memo, iterator, context =>
|
||||
return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
|
||||
_.each obj, (value, index, list) ->
|
||||
_.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) ->
|
||||
_.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) ->
|
||||
_.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) ->
|
||||
_.detect: obj, iterator, context =>
|
||||
result: null
|
||||
_.each obj, (value, index, list) ->
|
||||
_.each(obj) value, index, list =>
|
||||
if iterator.call(context, value, index, list)
|
||||
result: value
|
||||
_.breakLoop()
|
||||
@@ -106,97 +106,97 @@
|
||||
|
||||
# Return all the elements that pass a truth test. Use JavaScript 1.6's
|
||||
# filter(), if it exists.
|
||||
_.select: (obj, iterator, context) ->
|
||||
_.select: obj, iterator, context =>
|
||||
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context)
|
||||
results: []
|
||||
_.each obj, (value, index, list) ->
|
||||
_.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) ->
|
||||
_.reject: obj, iterator, context =>
|
||||
results: []
|
||||
_.each obj, (value, index, list) ->
|
||||
_.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) ->
|
||||
_.all: obj, iterator, context =>
|
||||
iterator ||= _.identity
|
||||
return obj.every(iterator, context) if obj and _.isFunction(obj.every)
|
||||
result: true
|
||||
_.each obj, (value, index, list) ->
|
||||
_.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) ->
|
||||
_.any: obj, iterator, context =>
|
||||
iterator ||= _.identity
|
||||
return obj.some(iterator, context) if obj and _.isFunction(obj.some)
|
||||
result: false
|
||||
_.each obj, (value, index, list) ->
|
||||
_.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) ->
|
||||
_.include: obj, target =>
|
||||
return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
|
||||
for key, val of obj
|
||||
for val in obj
|
||||
return true if val is target
|
||||
false
|
||||
|
||||
|
||||
# Invoke a method with arguments on every item in a collection.
|
||||
_.invoke: (obj, method) ->
|
||||
_.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]))
|
||||
_.pluck: obj, key =>
|
||||
_.map(obj, (val => val[key]))
|
||||
|
||||
|
||||
# Return the maximum item or (item-based computation).
|
||||
_.max: (obj, iterator, context) ->
|
||||
_.max: obj, iterator, context =>
|
||||
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
|
||||
result: {computed: -Infinity}
|
||||
_.each obj, (value, index, list) ->
|
||||
_.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) ->
|
||||
_.min: obj, iterator, context =>
|
||||
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
|
||||
result: {computed: Infinity}
|
||||
_.each obj, (value, index, list) ->
|
||||
_.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) ->
|
||||
_.sortBy: obj, iterator, context =>
|
||||
_.pluck(((_.map(obj) value, index, list =>
|
||||
{value: value, criteria: iterator.call(context, value, index, list)}
|
||||
).sort((left, right) ->
|
||||
).sort() left, right =>
|
||||
a: left.criteria; b: right.criteria
|
||||
if a < b then -1 else if a > b then 1 else 0
|
||||
)), 'value')
|
||||
), '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) ->
|
||||
_.sortedIndex: array, obj, iterator =>
|
||||
iterator ||= _.identity
|
||||
low: 0; high: array.length
|
||||
while low < high
|
||||
@@ -206,7 +206,7 @@
|
||||
|
||||
|
||||
# Convert anything iterable into a real, live array.
|
||||
_.toArray: (iterable) ->
|
||||
_.toArray: iterable =>
|
||||
return [] if (!iterable)
|
||||
return iterable.toArray() if (iterable.toArray)
|
||||
return iterable if (_.isArray(iterable))
|
||||
@@ -215,7 +215,7 @@
|
||||
|
||||
|
||||
# Return the number of elements in an object.
|
||||
_.size: (obj) -> _.toArray(obj).length
|
||||
_.size: obj => _.toArray(obj).length
|
||||
|
||||
|
||||
# -------------------------- Array Functions: ------------------------------
|
||||
@@ -223,7 +223,7 @@
|
||||
# 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) ->
|
||||
_.first: array, n, guard =>
|
||||
if n and not guard then slice.call(array, 0, n) else array[0]
|
||||
|
||||
|
||||
@@ -231,35 +231,35 @@
|
||||
# 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) ->
|
||||
_.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]
|
||||
_.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]
|
||||
_.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) ->
|
||||
_.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) ->
|
||||
_.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) ->
|
||||
_.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))
|
||||
@@ -268,27 +268,28 @@
|
||||
|
||||
# Produce an array that contains every item shared between all the
|
||||
# passed-in arrays.
|
||||
_.intersect: (array) ->
|
||||
_.intersect: array =>
|
||||
rest: _.rest(arguments)
|
||||
_.select _.uniq(array), (item) ->
|
||||
_.all rest, (other) ->
|
||||
_.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: ->
|
||||
length: _.max(_.pluck(arguments, 'length'))
|
||||
_.zip: =>
|
||||
args: _.toArray(arguments)
|
||||
length: _.max(_.pluck(args, 'length'))
|
||||
results: new Array(length)
|
||||
for i in [0...length]
|
||||
results[i]: _.pluck(arguments, String(i))
|
||||
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) ->
|
||||
_.indexOf: array, item =>
|
||||
return array.indexOf(item) if array.indexOf
|
||||
i: 0; l: array.length
|
||||
while l - i
|
||||
@@ -298,7 +299,7 @@
|
||||
|
||||
# Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
|
||||
# if possible.
|
||||
_.lastIndexOf: (array, item) ->
|
||||
_.lastIndexOf: array, item =>
|
||||
return array.lastIndexOf(item) if array.lastIndexOf
|
||||
i: array.length
|
||||
while i
|
||||
@@ -309,8 +310,8 @@
|
||||
# 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: arguments
|
||||
_.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];
|
||||
@@ -330,45 +331,45 @@
|
||||
|
||||
# Create a function bound to a given object (assigning 'this', and arguments,
|
||||
# optionally). Binding with arguments is also known as 'curry'.
|
||||
_.bind: (func, obj) ->
|
||||
_.bind: func, obj =>
|
||||
args: _.rest(arguments, 2)
|
||||
-> func.apply(obj or root, args.concat(arguments))
|
||||
=> 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) ->
|
||||
_.bindAll: obj =>
|
||||
funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
|
||||
_.each(funcs, (f) -> obj[f]: _.bind(obj[f], 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) ->
|
||||
_.delay: func, wait =>
|
||||
args: _.rest(arguments, 2)
|
||||
setTimeout((-> func.apply(func, args)), wait)
|
||||
setTimeout((=> func.apply(func, args)), wait)
|
||||
|
||||
|
||||
# Defers a function, scheduling it to run after the current call stack has
|
||||
# cleared.
|
||||
_.defer: (func) ->
|
||||
_.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(arguments))
|
||||
_.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: arguments
|
||||
->
|
||||
args: arguments
|
||||
_.compose: =>
|
||||
funcs: _.toArray(arguments)
|
||||
=>
|
||||
args: _.toArray(arguments)
|
||||
for i in [(funcs.length - 1)..0]
|
||||
args: [funcs[i].apply(this, args)]
|
||||
args[0]
|
||||
@@ -377,43 +378,43 @@
|
||||
# ------------------------- Object Functions: ----------------------------
|
||||
|
||||
# Retrieve the names of an object's properties.
|
||||
_.keys: (obj) ->
|
||||
_.keys: obj =>
|
||||
return _.range(0, obj.length) if _.isArray(obj)
|
||||
key for key, val of obj
|
||||
key for val, key in obj
|
||||
|
||||
|
||||
# Retrieve the values of an object's properties.
|
||||
_.values: (obj) ->
|
||||
_.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()
|
||||
_.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
|
||||
_.extend: destination, source =>
|
||||
for val, key in source
|
||||
destination[key]: val
|
||||
destination
|
||||
|
||||
|
||||
# Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone: (obj) ->
|
||||
_.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) ->
|
||||
_.tap: obj, interceptor =>
|
||||
interceptor(obj)
|
||||
obj
|
||||
|
||||
|
||||
# Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual: (a, b) ->
|
||||
_.isEqual: a, b =>
|
||||
# Check object identity.
|
||||
return true if a is b
|
||||
# Different types?
|
||||
@@ -449,104 +450,93 @@
|
||||
|
||||
|
||||
# Is a given array or object empty?
|
||||
_.isEmpty: (obj) -> _.keys(obj).length is 0
|
||||
_.isEmpty: obj => _.keys(obj).length is 0
|
||||
|
||||
|
||||
# Is a given value a DOM element?
|
||||
_.isElement: (obj) -> obj and obj.nodeType is 1
|
||||
_.isElement: obj => obj and obj.nodeType is 1
|
||||
|
||||
|
||||
# Is a given value an array?
|
||||
_.isArray: (obj) -> !!(obj and obj.concat and obj.unshift)
|
||||
_.isArray: obj => !!(obj and obj.concat and obj.unshift)
|
||||
|
||||
|
||||
# Is a given variable an arguments object?
|
||||
_.isArguments: (obj) -> obj and _.isNumber(obj.length) and not obj.concat and
|
||||
not obj.substr and not obj.apply and not propertyIsEnumerable.call(obj, 'length')
|
||||
_.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)
|
||||
_.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))
|
||||
_.isString: obj => !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
|
||||
|
||||
|
||||
# Is a given value a number?
|
||||
_.isNumber: (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
|
||||
_.isNumber: obj => toString.call(obj) is '[object Number]'
|
||||
|
||||
|
||||
# Is a given value a Date?
|
||||
_.isDate: (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
|
||||
_.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))
|
||||
_.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)
|
||||
_.isNaN: obj => _.isNumber(obj) and window.isNaN(obj)
|
||||
|
||||
|
||||
# Is a given value equal to null?
|
||||
_.isNull: (obj) -> obj is null
|
||||
_.isNull: obj => obj is null
|
||||
|
||||
|
||||
# Is a given variable undefined?
|
||||
_.isUndefined: (obj) -> typeof obj is '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: ->
|
||||
_.noConflict: =>
|
||||
root._: previousUnderscore
|
||||
this
|
||||
|
||||
|
||||
# Keep the identity function around for default iterators.
|
||||
_.identity: (value) -> value
|
||||
_.identity: value => value
|
||||
|
||||
|
||||
# Break out of the middle of an iteration.
|
||||
_.breakLoop: -> throw breaker
|
||||
_.breakLoop: => throw breaker
|
||||
|
||||
|
||||
# Generate a unique integer id (unique within the entire client session).
|
||||
# Useful for temporary DOM ids.
|
||||
idCounter: 0
|
||||
_.uniqueId: (prefix) ->
|
||||
_.uniqueId: prefix =>
|
||||
(prefix or '') + idCounter++
|
||||
|
||||
|
||||
# By default, Underscore uses ERB-style template delimiters, change the
|
||||
# following template settings to use alternative delimiters.
|
||||
_.templateSettings: {
|
||||
start: '<%'
|
||||
end: '%>'
|
||||
interpolate: /<%=(.+?)%>/g
|
||||
}
|
||||
|
||||
|
||||
# JavaScript templating a-la ERB, pilfered from John Resig's
|
||||
# "Secrets of the JavaScript Ninja", page 83.
|
||||
# Single-quotea fix from Rick Strahl's version.
|
||||
_.template: (str, data) ->
|
||||
c: _.templateSettings
|
||||
fn: new Function 'obj',
|
||||
_.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, " ")
|
||||
.replace(new RegExp("'(?=[^"+c.end[0]+"]*"+c.end+")","g"),"\t")
|
||||
.split("'").join("\\'")
|
||||
.split("\t").join("'")
|
||||
.replace(c.interpolate, "',$1,'")
|
||||
.split(c.start).join("');")
|
||||
.split(c.end).join("p.push('") +
|
||||
"');}return p.join('');"
|
||||
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
|
||||
|
||||
|
||||
@@ -566,38 +556,38 @@
|
||||
# /*------------------------ Setup the OOP Wrapper: --------------------------*/
|
||||
|
||||
# Helper function to continue chaining intermediate results.
|
||||
result: (obj, chain) ->
|
||||
result: obj, chain =>
|
||||
if chain then _(obj).chain() else obj
|
||||
|
||||
|
||||
# Add all of the Underscore functions to the wrapper object.
|
||||
_.each _.functions(_), (name) ->
|
||||
_.each(_.functions(_)) name =>
|
||||
method: _[name]
|
||||
wrapper.prototype[name]: ->
|
||||
wrapper.prototype[name]: =>
|
||||
unshift.call(arguments, this._wrapped)
|
||||
result(method.apply(_, arguments), this._chain)
|
||||
|
||||
|
||||
# Add all mutator Array functions to the wrapper.
|
||||
_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->
|
||||
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift']) name =>
|
||||
method: Array.prototype[name]
|
||||
wrapper.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) ->
|
||||
_.each(['concat', 'join', 'slice']) name =>
|
||||
method: Array.prototype[name]
|
||||
wrapper.prototype[name]: ->
|
||||
wrapper.prototype[name]: =>
|
||||
result(method.apply(this._wrapped, arguments), this._chain)
|
||||
|
||||
|
||||
# Start chaining a wrapped Underscore object.
|
||||
wrapper::chain: ->
|
||||
wrapper.prototype.chain: =>
|
||||
this._chain: true
|
||||
this
|
||||
|
||||
|
||||
# Extracts the result from a wrapped and chained object.
|
||||
wrapper::value: -> this._wrapped
|
||||
wrapper.prototype.value: => this._wrapped
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
This folder includes rough cuts of CoffeeScript syntax highlighters for
|
||||
TextMate and Vim. Improvements to their lexing ability are always welcome.
|
||||
|
||||
To install the TextMate bundle, run `bin/coffee --install-bundle`, or drop it
|
||||
into "~/Library/Application Support/TextMate/Bundles".
|
||||
|
||||
To install the Vim highlighter, copy "coffee.vim" into the "syntax" directory of
|
||||
your vim72, and enable it in either of the following two ways:
|
||||
|
||||
* Manually, by running `:set syntax=coffee`
|
||||
|
||||
* Or automatically, by creating a "filetype.vim" file within "~/.vim", which
|
||||
contains something along these lines:
|
||||
|
||||
if exists("did_load_filetypes")
|
||||
finish
|
||||
end
|
||||
augroup filetypedetect
|
||||
au! BufRead,BufNewFile *.coffee setfiletype coffee
|
||||
augroup END
|
||||
@@ -1,111 +0,0 @@
|
||||
" Vim syntax file
|
||||
" Language: CoffeeScript
|
||||
" Maintainer: Jeff Olson <olson.jeffery@gmail.com>
|
||||
" URL: http://github.com/olsonjeffery
|
||||
" Changes: (jro) initial port from javascript
|
||||
" Last Change: 2006 Jun 19
|
||||
" Adaptation of javascript.vim syntax file (distro'd w/ vim72),
|
||||
" maintained by Claudio Fleiner <claudio@fleiner.com>
|
||||
" with updates from Scott Shattuck (ss) <ss@technicalpursuit.com>
|
||||
|
||||
if !exists("main_syntax")
|
||||
if version < 600
|
||||
syntax clear
|
||||
elseif exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
let main_syntax = 'coffee'
|
||||
endif
|
||||
|
||||
syn case ignore
|
||||
|
||||
syn match coffeeLineComment "#.*" contains=@Spell,CoffeeCommentTodo
|
||||
syn match coffeeSpecial "\\\d\d\d\|\\."
|
||||
syn region coffeeStringD start=+"+ skip=+\\\\\|\\"+ end=+"\|$+ contains=coffeeSpecial,@htmlPreproc
|
||||
syn region coffeeStringS start=+'+ skip=+\\\\\|\\'+ end=+'\|$+ contains=coffeeSpecial,@htmlPreproc
|
||||
|
||||
syn match coffeeSpecialCharacter "'\\.'"
|
||||
syn match coffeeNumber "-\=\<\d\+L\=\>\|0[xX][0-9a-fA-F]\+\>"
|
||||
syn region coffeeRegexpString start=+/[^/*]+me=e-1 skip=+\\\\\|\\/+ end=+/[gi]\{0,2\}\s*$+ end=+/[gi]\{0,2\}\s*[;.,)\]}]+me=e-1 contains=@htmlPreproc oneline
|
||||
|
||||
syn match coffeePrototypeAccess "::"
|
||||
syn match coffeeFunction "->"
|
||||
|
||||
syn keyword coffeeExtends extends
|
||||
syn keyword coffeeConditional if else switch then
|
||||
syn keyword coffeeRepeat while for in of
|
||||
syn keyword coffeeBranch break continue
|
||||
syn keyword coffeeOperator delete instanceof typeof
|
||||
syn keyword coffeeType Array Boolean Date Function Number Object String RegExp
|
||||
syn keyword coffeeStatement return with
|
||||
syn keyword coffeeBoolean true false
|
||||
syn keyword coffeeNull null undefined
|
||||
syn keyword coffeeIdentifier arguments this var
|
||||
syn keyword coffeeLabel case default
|
||||
syn keyword coffeeException try catch finally throw
|
||||
syn keyword coffeeMessage alert confirm prompt status
|
||||
syn keyword coffeeGlobal self window top parent
|
||||
syn keyword coffeeMember document event location
|
||||
syn keyword coffeeDeprecated escape unescape
|
||||
syn keyword coffeeReserved abstract boolean byte char class const debugger double enum export final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile
|
||||
|
||||
syn sync fromstart
|
||||
syn sync maxlines=100
|
||||
|
||||
if main_syntax == "coffee"
|
||||
syn sync ccomment coffeeComment
|
||||
endif
|
||||
|
||||
" Define the default highlighting.
|
||||
" For version 5.7 and earlier: only when not done already
|
||||
" For version 5.8 and later: only when an item doesn't have highlighting yet
|
||||
if version >= 508 || !exists("did_coffee_syn_inits")
|
||||
if version < 508
|
||||
let did_coffee_syn_inits = 1
|
||||
command -nargs=+ HiLink hi link <args>
|
||||
else
|
||||
command -nargs=+ HiLink hi def link <args>
|
||||
endif
|
||||
HiLink coffeePrototypeAccess Keyword
|
||||
HiLink coffeeExtends Keyword
|
||||
HiLink coffeeLineComment Comment
|
||||
HiLink coffeeSpecial Special
|
||||
HiLink coffeeStringS String
|
||||
HiLink coffeeStringD String
|
||||
HiLink coffeeCharacter Character
|
||||
HiLink coffeeSpecialCharacter coffeeSpecial
|
||||
HiLink coffeeNumber coffeeValue
|
||||
HiLink coffeeConditional Conditional
|
||||
HiLink coffeeRepeat Repeat
|
||||
HiLink coffeeBranch Conditional
|
||||
HiLink coffeeOperator Operator
|
||||
HiLink coffeeType Type
|
||||
HiLink coffeeStatement Statement
|
||||
HiLink coffeeFunction Function
|
||||
HiLink coffeeBraces Function
|
||||
HiLink coffeeError Error
|
||||
HiLink coffeeScrParenError coffeeError
|
||||
HiLink coffeeNull Keyword
|
||||
HiLink coffeeBoolean Boolean
|
||||
HiLink coffeeRegexpString String
|
||||
|
||||
HiLink coffeeIdentifier Identifier
|
||||
HiLink coffeeLabel Label
|
||||
HiLink coffeeException Exception
|
||||
HiLink coffeeMessage Keyword
|
||||
HiLink coffeeGlobal Keyword
|
||||
HiLink coffeeMember Keyword
|
||||
HiLink coffeeDeprecated Exception
|
||||
HiLink coffeeReserved Keyword
|
||||
HiLink coffeeDebug Debug
|
||||
HiLink coffeeConstant Label
|
||||
|
||||
delcommand HiLink
|
||||
endif
|
||||
|
||||
let b:current_syntax = "coffee"
|
||||
if main_syntax == 'coffee'
|
||||
unlet main_syntax
|
||||
endif
|
||||
|
||||
" vim: ts=8
|
||||
1060
index.html
1060
index.html
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ require "coffee_script/parse_error"
|
||||
# Namespace for all CoffeeScript internal classes.
|
||||
module CoffeeScript
|
||||
|
||||
VERSION = '0.3.2' # Keep in sync with the gemspec.
|
||||
VERSION = '0.2.0' # Keep in sync with the gemspec.
|
||||
|
||||
# Compile a script (String or IO) to JavaScript.
|
||||
def self.compile(script, options={})
|
||||
|
||||
@@ -10,40 +10,57 @@
|
||||
</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>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.function.coffee</string>
|
||||
<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>5</key>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>storage.type.function.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>comment</key>
|
||||
<string>match stuff like: a -> … </string>
|
||||
<string>match stuff like: a => … </string>
|
||||
<key>match</key>
|
||||
<string>(\()([a-zA-Z0-9_?.$]*(,\s*[a-zA-Z0-9_?.$]+)*)(\))\s*((=|-)>)</string>
|
||||
<string>([a-zA-Z0-9_?., $*]*)\s*(=>)</string>
|
||||
<key>name</key>
|
||||
<string>meta.inline.function.coffee</string>
|
||||
</dict>
|
||||
@@ -72,36 +89,6 @@
|
||||
<key>name</key>
|
||||
<string>constant.numeric.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>(@)[a-zA-Z_$]\w*</string>
|
||||
<key>name</key>
|
||||
<string>variable.other.readwrite.instance.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>
|
||||
@@ -215,48 +202,15 @@
|
||||
<key>name</key>
|
||||
<string>comment.line.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>\b(break|by|catch|continue|else|finally|for|in|of|if|return|switch|then|throw|try|unless|when|while)\b</string>
|
||||
<string>\b(break|by|catch|continue|else|finally|for|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>
|
||||
<string>\b([a-zA-Z$_]\w*)(\:)\s</string>
|
||||
<key>name</key>
|
||||
<string>variable.assignment.coffee</string>
|
||||
<key>captures</key>
|
||||
@@ -266,6 +220,11 @@
|
||||
<key>name</key>
|
||||
<string>entity.name.function.coffee</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -300,13 +259,7 @@
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>(=|-)></string>
|
||||
<key>name</key>
|
||||
<string>storage.type.function.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>!|%|&|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
|
||||
<string>!|\$|%|&|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.coffee</string>
|
||||
</dict>
|
||||
@@ -316,6 +269,39 @@
|
||||
<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>
|
||||
@@ -1,50 +0,0 @@
|
||||
(function(){
|
||||
var compiler, path;
|
||||
// Executes the `coffee` Ruby program to convert from CoffeeScript to JavaScript.
|
||||
path = require('path');
|
||||
// The path to the CoffeeScript executable.
|
||||
compiler = path.normalize(path.dirname(__filename) + '/../../bin/coffee');
|
||||
// Compile a string over stdin, with global variables, for the REPL.
|
||||
exports.compile = function compile(code, callback) {
|
||||
var coffee, js;
|
||||
js = '';
|
||||
coffee = process.createChildProcess(compiler, ['--eval', '--no-wrap', '--globals']);
|
||||
coffee.addListener('output', function(results) {
|
||||
if ((typeof results !== "undefined" && results !== null)) {
|
||||
return js += results;
|
||||
}
|
||||
});
|
||||
coffee.addListener('exit', function() {
|
||||
return callback(js);
|
||||
});
|
||||
coffee.write(code);
|
||||
return coffee.close();
|
||||
};
|
||||
// Compile a list of CoffeeScript files on disk.
|
||||
exports.compile_files = function compile_files(paths, callback) {
|
||||
var coffee, exit_ran, js;
|
||||
js = '';
|
||||
coffee = process.createChildProcess(compiler, ['--print'].concat(paths));
|
||||
coffee.addListener('output', function(results) {
|
||||
if ((typeof results !== "undefined" && results !== null)) {
|
||||
return js += results;
|
||||
}
|
||||
});
|
||||
// NB: we have to add a mutex to make sure it doesn't get called twice.
|
||||
exit_ran = false;
|
||||
coffee.addListener('exit', function() {
|
||||
if (exit_ran) {
|
||||
return null;
|
||||
}
|
||||
exit_ran = true;
|
||||
return callback(js);
|
||||
});
|
||||
return coffee.addListener('error', function(message) {
|
||||
if (!(message)) {
|
||||
return null;
|
||||
}
|
||||
puts(message);
|
||||
throw new Error("CoffeeScript compile error");
|
||||
});
|
||||
};
|
||||
})();
|
||||
@@ -1,13 +1,7 @@
|
||||
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
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../coffee-script')
|
||||
|
||||
module CoffeeScript
|
||||
|
||||
@@ -25,14 +19,9 @@ Usage:
|
||||
# Seconds to pause between checks for changed source files.
|
||||
WATCH_INTERVAL = 0.5
|
||||
|
||||
# Path to the root of the CoffeeScript install.
|
||||
ROOT = File.expand_path(File.dirname(__FILE__) + '/../..')
|
||||
|
||||
# Commands to execute CoffeeScripts.
|
||||
RUNNERS = {
|
||||
:node => "node #{ROOT}/lib/coffee_script/runner.js",
|
||||
:narwhal => "narwhal -p #{ROOT} -e 'require(\"coffee-script\").run(system.args);'"
|
||||
}
|
||||
# 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
|
||||
@@ -117,20 +106,20 @@ Usage:
|
||||
puts js
|
||||
end
|
||||
|
||||
# Use Node.js or Narwhal to run an interactive CoffeeScript session.
|
||||
# Use Narwhal to run an interactive CoffeeScript session.
|
||||
def launch_repl
|
||||
exec "#{RUNNERS[@options[:runner]]}"
|
||||
exec "#{LAUNCHER}"
|
||||
rescue Errno::ENOENT
|
||||
puts "Error: #{@options[:runner]} must be installed to use the interactive REPL."
|
||||
puts "Error: Narwhal must be installed to use the interactive REPL."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
# Use Node.js or Narwhal to compile and execute CoffeeScripts.
|
||||
# Use Narwhal to compile and execute CoffeeScripts.
|
||||
def run_scripts
|
||||
sources = @sources.join(' ')
|
||||
exec "#{RUNNERS[@options[:runner]]} #{sources}"
|
||||
exec "#{LAUNCHER} #{sources}"
|
||||
rescue Errno::ENOENT
|
||||
puts "Error: #{@options[:runner]} must be installed in order to execute scripts."
|
||||
puts "Error: Narwhal must be installed in order to execute CoffeeScripts."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
@@ -144,9 +133,8 @@ Usage:
|
||||
begin
|
||||
options = {}
|
||||
options[:no_wrap] = true if @options[:no_wrap]
|
||||
options[:globals] = true if @options[:globals]
|
||||
CoffeeScript.compile(script, options)
|
||||
rescue CoffeeScript::ParseError => e
|
||||
rescue CoffeeScript::ParseError, SyntaxError => e
|
||||
STDERR.puts "#{source}: #{e.message}"
|
||||
exit(1) unless @options[:watch]
|
||||
nil
|
||||
@@ -164,17 +152,17 @@ Usage:
|
||||
# Install the CoffeeScript TextMate bundle to ~/Library.
|
||||
def install_bundle
|
||||
bundle_dir = File.expand_path('~/Library/Application Support/TextMate/Bundles/')
|
||||
FileUtils.cp_r("#{ROOT}/extras/CoffeeScript.tmbundle", bundle_dir)
|
||||
FileUtils.cp_r(File.dirname(__FILE__) + '/CoffeeScript.tmbundle', bundle_dir)
|
||||
end
|
||||
|
||||
# Use OptionParser for all the options.
|
||||
def parse_options
|
||||
@options = {:runner => :node}
|
||||
@options = {}
|
||||
@option_parser = OptionParser.new do |opts|
|
||||
opts.on('-i', '--interactive', 'run an interactive CoffeeScript REPL') do |i|
|
||||
opts.on('-i', '--interactive', 'run a CoffeeScript REPL (requires Narwhal)') do |i|
|
||||
@options[:interactive] = true
|
||||
end
|
||||
opts.on('-r', '--run', 'compile and run a CoffeeScript') do |r|
|
||||
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|
|
||||
@@ -199,15 +187,9 @@ Usage:
|
||||
opts.on('-v', '--verbose', 'print at every step of code generation') do |v|
|
||||
ENV['VERBOSE'] = 'true'
|
||||
end
|
||||
opts.on('-n', '--no-wrap', 'raw output, no function safety wrapper') do |n|
|
||||
opts.on('-n', '--no-wrap', 'raw output, no safety wrapper or vars') 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('--narwhal', 'use Narwhal instead of Node.js') do |n|
|
||||
@options[:runner] = :narwhal
|
||||
end
|
||||
opts.on_tail('--install-bundle', 'install the CoffeeScript TextMate bundle') do |i|
|
||||
install_bundle
|
||||
exit
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
class Parser
|
||||
|
||||
# Declare terminal tokens produced by the lexer.
|
||||
# Declare 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 SOAK_ACCESS
|
||||
token CODE PARAM_START PARAM PARAM_END NEW RETURN
|
||||
token CALL_START CALL_END INDEX_START INDEX_END
|
||||
token IDENTIFIER PROPERTY_ACCESS
|
||||
token CODE PARAM PARAM_SPLAT NEW RETURN
|
||||
token TRY CATCH FINALLY THROW
|
||||
token BREAK CONTINUE
|
||||
token FOR IN OF BY WHEN WHILE
|
||||
token FOR IN BY WHEN WHILE
|
||||
token SWITCH LEADING_WHEN
|
||||
token DELETE INSTANCEOF TYPEOF
|
||||
token SUPER EXTENDS
|
||||
token ASSIGN RETURN
|
||||
token NEWLINE
|
||||
token COMMENT
|
||||
token JS
|
||||
@@ -21,22 +19,25 @@ token INDENT OUTDENT
|
||||
|
||||
# Declare order of operations.
|
||||
prechigh
|
||||
nonassoc UMINUS UPLUS NOT '!' '!!' '~' '++' '--'
|
||||
left '*' '/' '%' '?' '.'
|
||||
left '?'
|
||||
nonassoc UMINUS PARAM_SPLAT SPLAT NOT '!' '!!' '~' '++' '--'
|
||||
left '*' '/' '%'
|
||||
left '+' '-'
|
||||
left '<<' '>>' '>>>' '&' '|' '^'
|
||||
left '<<' '>>' '>>>'
|
||||
left '&' '|' '^'
|
||||
left '<=' '<' '>' '>='
|
||||
right '==' '!=' IS ISNT
|
||||
left '&&' '||' AND OR
|
||||
right '-=' '+=' '/=' '*=' '%=' '||=' '&&=' '?='
|
||||
right '-=' '+=' '/=' '*=' '%='
|
||||
right DELETE INSTANCEOF TYPEOF
|
||||
left '.'
|
||||
right INDENT
|
||||
left OUTDENT
|
||||
right WHEN LEADING_WHEN IN OF BY
|
||||
right WHEN LEADING_WHEN IN BY
|
||||
right THROW FOR NEW SUPER
|
||||
left EXTENDS
|
||||
right ASSIGN RETURN
|
||||
right '->' '=>' UNLESS IF ELSE WHILE
|
||||
left ASSIGN '||=' '&&='
|
||||
right RETURN '=>' UNLESS IF ELSE WHILE
|
||||
preclow
|
||||
|
||||
rule
|
||||
@@ -56,13 +57,13 @@ rule
|
||||
| Expressions Terminator { result = val[0] }
|
||||
;
|
||||
|
||||
# All types of expressions in our language. The basic unit of CoffeeScript
|
||||
# is the expression.
|
||||
# All types of expressions in our language.
|
||||
Expression:
|
||||
Value
|
||||
| Call
|
||||
| Code
|
||||
| Operation
|
||||
| Range
|
||||
| Assign
|
||||
| If
|
||||
| Try
|
||||
@@ -77,20 +78,18 @@ rule
|
||||
| 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.
|
||||
# All tokens that can terminate an expression.
|
||||
Terminator:
|
||||
"\n"
|
||||
| ";"
|
||||
;
|
||||
|
||||
# All hard-coded values. These can be printed straight to JavaScript.
|
||||
# All hard-coded values.
|
||||
Literal:
|
||||
NUMBER { result = LiteralNode.new(val[0]) }
|
||||
| STRING { result = LiteralNode.new(val[0]) }
|
||||
@@ -98,20 +97,20 @@ rule
|
||||
| REGEX { result = LiteralNode.new(val[0]) }
|
||||
| BREAK { result = LiteralNode.new(val[0]) }
|
||||
| CONTINUE { result = LiteralNode.new(val[0]) }
|
||||
| TRUE { result = LiteralNode.new(Value.new(true)) }
|
||||
| FALSE { result = LiteralNode.new(Value.new(false)) }
|
||||
| YES { result = LiteralNode.new(Value.new(true)) }
|
||||
| NO { result = LiteralNode.new(Value.new(false)) }
|
||||
| ON { result = LiteralNode.new(Value.new(true)) }
|
||||
| OFF { result = LiteralNode.new(Value.new(false)) }
|
||||
| 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).
|
||||
# Assignment to a variable.
|
||||
Assign:
|
||||
Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
|
||||
;
|
||||
|
||||
# Assignment within an object literal (can be quoted).
|
||||
# Assignment within an object literal.
|
||||
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) }
|
||||
@@ -121,7 +120,6 @@ rule
|
||||
# A return statement.
|
||||
Return:
|
||||
RETURN Expression { result = ReturnNode.new(val[1]) }
|
||||
| RETURN { result = ReturnNode.new(ValueNode.new(Value.new('null'))) }
|
||||
;
|
||||
|
||||
# A comment.
|
||||
@@ -136,7 +134,6 @@ rule
|
||||
'!' 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]) }
|
||||
| '+' Expression = UPLUS { 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]) }
|
||||
@@ -175,7 +172,6 @@ rule
|
||||
| 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]) }
|
||||
@@ -184,28 +180,18 @@ rule
|
||||
| Expression '%=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '?=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression 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:
|
||||
PARAM_START ParamList PARAM_END
|
||||
FuncGlyph Block { result = CodeNode.new(val[1], val[4], val[3]) }
|
||||
| FuncGlyph Block { result = CodeNode.new([], val[1], val[0]) }
|
||||
;
|
||||
|
||||
# The symbols to signify functions, and bound functions.
|
||||
FuncGlyph:
|
||||
'->' { result = :func }
|
||||
| '=>' { result = :boundfunc }
|
||||
ParamList "=>" Block { result = CodeNode.new(val[0], val[2]) }
|
||||
| "=>" Block { result = CodeNode.new([], val[1]) }
|
||||
;
|
||||
|
||||
# The parameters to a function definition.
|
||||
@@ -214,15 +200,13 @@ rule
|
||||
| ParamList "," Param { result = val[0] << val[2] }
|
||||
;
|
||||
|
||||
# A Parameter (or ParamSplat) in a function definition.
|
||||
Param:
|
||||
PARAM
|
||||
| PARAM "." "." "." { result = SplatNode.new(val[0]) }
|
||||
| PARAM_SPLAT PARAM { result = ParamSplatNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# A regular splat.
|
||||
Splat:
|
||||
Expression "." "." "." { result = SplatNode.new(val[0]) }
|
||||
'*' Value = SPLAT { result = ArgSplatNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# Expressions that can be treated as values.
|
||||
@@ -232,8 +216,6 @@ rule
|
||||
| Array { result = ValueNode.new(val[0]) }
|
||||
| Object { result = ValueNode.new(val[0]) }
|
||||
| Parenthetical { result = ValueNode.new(val[0]) }
|
||||
| Range { result = ValueNode.new(val[0]) }
|
||||
| This { result = ValueNode.new(val[0]) }
|
||||
| Value Accessor { result = val[0] << val[1] }
|
||||
| Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
|
||||
;
|
||||
@@ -241,15 +223,13 @@ rule
|
||||
# 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], :prototype) }
|
||||
| SOAK_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], :soak) }
|
||||
| Index { result = val[0] }
|
||||
| Slice { result = SliceNode.new(val[0]) }
|
||||
| Range { result = SliceNode.new(val[0]) }
|
||||
;
|
||||
|
||||
# Indexing into an object or array.
|
||||
Index:
|
||||
INDEX_START Expression INDEX_END { result = IndexNode.new(val[1]) }
|
||||
"[" Expression "]" { result = IndexNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# An object literal.
|
||||
@@ -284,22 +264,17 @@ rule
|
||||
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:
|
||||
CALL_START ArgList CALL_END { result = val[1] }
|
||||
"(" ArgList ")" { result = val[1] }
|
||||
| "(" ArgList ")" Code { result = val[1] << val[3] }
|
||||
;
|
||||
|
||||
# Calling super.
|
||||
Super:
|
||||
SUPER CALL_START ArgList CALL_END { result = CallNode.new(Value.new('super'), val[2]) }
|
||||
;
|
||||
|
||||
# This references, either naked or to a property.
|
||||
This:
|
||||
'@' { result = ThisNode.new }
|
||||
| '@' IDENTIFIER { result = ThisNode.new(val[1]) }
|
||||
SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
|
||||
;
|
||||
|
||||
# The range literal.
|
||||
@@ -310,14 +285,6 @@ rule
|
||||
"." "." "." Expression "]" { result = RangeNode.new(val[1], val[5], true) }
|
||||
;
|
||||
|
||||
# The slice literal.
|
||||
Slice:
|
||||
INDEX_START Expression "." "."
|
||||
Expression INDEX_END { result = RangeNode.new(val[1], val[4]) }
|
||||
| INDEX_START Expression "." "." "."
|
||||
Expression INDEX_END { result = RangeNode.new(val[1], val[5], true) }
|
||||
;
|
||||
|
||||
# The array literal.
|
||||
Array:
|
||||
"[" ArgList "]" { result = ArrayNode.new(val[1]) }
|
||||
@@ -335,12 +302,6 @@ rule
|
||||
| ArgList OUTDENT { result = val[0] }
|
||||
;
|
||||
|
||||
# Just simple, comma-separated, required arguments (no fancy syntax).
|
||||
SimpleArgs:
|
||||
Expression { result = val[0] }
|
||||
| SimpleArgs "," Expression { result = ([val[0]] << val[2]).flatten }
|
||||
;
|
||||
|
||||
# Try/catch/finally exception handling blocks.
|
||||
Try:
|
||||
TRY Block Catch { result = TryNode.new(val[1], val[2][0], val[2][1]) }
|
||||
@@ -367,8 +328,6 @@ rule
|
||||
# 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) }
|
||||
| Expression WHILE Expression { result = WhileNode.new(val[2], Expressions.wrap(val[0])) }
|
||||
;
|
||||
|
||||
# Array comprehensions, including guard and current index.
|
||||
@@ -388,7 +347,6 @@ rule
|
||||
# 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
|
||||
@@ -411,13 +369,15 @@ rule
|
||||
|
||||
# An individual when.
|
||||
When:
|
||||
LEADING_WHEN SimpleArgs Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
|
||||
| LEADING_WHEN SimpleArgs Block
|
||||
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]) }
|
||||
| Comment
|
||||
;
|
||||
|
||||
# The most basic form of "if".
|
||||
# All of the following nutso if-else destructuring is to make the
|
||||
# grammar expand unambiguously.
|
||||
|
||||
IfBlock:
|
||||
IF Expression Block { result = IfNode.new(val[1], val[2]) }
|
||||
;
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
(function(){
|
||||
var ASSIGNMENT, CALLABLE, CODE, COMMENT, COMMENT_CLEANER, HEREDOC, HEREDOC_INDENT, IDENTIFIER, JS, JS_CLEANER, KEYWORDS, LAST_DENT, LAST_DENTS, MULTILINER, MULTI_DENT, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, Rewriter, STRING, STRING_NEWLINES, WHITESPACE, lex;
|
||||
Rewriter = require('./rewriter').Rewriter;
|
||||
// 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.
|
||||
exports.Lexer = (lex = function lex() { });
|
||||
// Constants ============================================================
|
||||
// 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", "arguments", "try", "catch", "finally", "throw", "break", "continue", "for", "in", "of", "by", "where", "while", "delete", "instanceof", "typeof", "switch", "when", "super", "extends"];
|
||||
// Token matching regexes.
|
||||
IDENTIFIER = /^([a-zA-Z$_](\w|\$)*)/;
|
||||
NUMBER = /^(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i;
|
||||
STRING = /^(""|''|"([\s\S]*?)([^\\]|\\\\)"|'([\s\S]*?)([^\\]|\\\\)')/;
|
||||
HEREDOC = /^("{6}|'{6}|"{3}\n?([\s\S]*?)\n?([ \t]*)"{3}|'{3}\n?([\s\S]*?)\n?([ \t]*)'{3})/;
|
||||
JS = /^(``|`([\s\S]*?)([^\\]|\\\\)`)/;
|
||||
OPERATOR = /^([+\*&|\/\-%=<>:!?]+)/;
|
||||
WHITESPACE = /^([ \t]+)/;
|
||||
COMMENT = /^(((\n?[ \t]*)?#.*$)+)/;
|
||||
CODE = /^((-|=)>)/;
|
||||
REGEX = /^(\/(.*?)([^\\]|\\\\)\/[imgy]{0,4})/;
|
||||
MULTI_DENT = /^((\n([ \t]*))+)(\.)?/;
|
||||
LAST_DENTS = /\n([ \t]*)/g;
|
||||
LAST_DENT = /\n([ \t]*)/;
|
||||
ASSIGNMENT = /^(:|=)$/;
|
||||
// Token cleaning regexes.
|
||||
JS_CLEANER = /(^`|`$)/g;
|
||||
MULTILINER = /\n/g;
|
||||
STRING_NEWLINES = /\n[ \t]*/g;
|
||||
COMMENT_CLEANER = /(^[ \t]*#|\n[ \t]*$)/mg;
|
||||
NO_NEWLINE = /^([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/;
|
||||
HEREDOC_INDENT = /^[ \t]+/g;
|
||||
// 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', 'TRUE'];
|
||||
// Tokens which could legitimately be invoked or indexed.
|
||||
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING'];
|
||||
// Scan by attempting to match tokens one character at a time. Slow and steady.
|
||||
lex.prototype.tokenize = function tokenize(code) {
|
||||
this.code = code;
|
||||
// Cleanup code by remove extra line breaks, TODO: chomp
|
||||
this.i = 0;
|
||||
// Current character position we're parsing
|
||||
this.line = 1;
|
||||
// The current line.
|
||||
this.indent = 0;
|
||||
// The current indent level.
|
||||
this.indents = [];
|
||||
// The stack of all indent levels we are currently within.
|
||||
this.tokens = [];
|
||||
// Collection of all parsed tokens in the form [:TOKEN_TYPE, value]
|
||||
this.spaced = null;
|
||||
// The last token that has a space following it.
|
||||
while (this.i < this.code.length) {
|
||||
this.chunk = this.code.slice(this.i);
|
||||
this.extract_next_token();
|
||||
}
|
||||
this.close_indentation();
|
||||
return (new Rewriter()).rewrite(this.tokens);
|
||||
};
|
||||
// At every position, run through this list of attempted matches,
|
||||
// short-circuiting if any of them succeed.
|
||||
lex.prototype.extract_next_token = function extract_next_token() {
|
||||
if (this.identifier_token()) {
|
||||
return null;
|
||||
}
|
||||
if (this.number_token()) {
|
||||
return null;
|
||||
}
|
||||
if (this.heredoc_token()) {
|
||||
return null;
|
||||
}
|
||||
if (this.string_token()) {
|
||||
return null;
|
||||
}
|
||||
if (this.js_token()) {
|
||||
return null;
|
||||
}
|
||||
if (this.regex_token()) {
|
||||
return null;
|
||||
}
|
||||
if (this.indent_token()) {
|
||||
return null;
|
||||
}
|
||||
if (this.comment_token()) {
|
||||
return null;
|
||||
}
|
||||
if (this.whitespace_token()) {
|
||||
return null;
|
||||
}
|
||||
return this.literal_token();
|
||||
};
|
||||
// Tokenizers ==========================================================
|
||||
// Matches identifying literals: variables, keywords, method names, etc.
|
||||
lex.prototype.identifier_token = function identifier_token() {
|
||||
var id, tag;
|
||||
if (!((id = this.match(IDENTIFIER, 1)))) {
|
||||
return false;
|
||||
}
|
||||
// Keywords are special identifiers tagged with their own name,
|
||||
// 'if' will result in an ['IF', "if"] token.
|
||||
tag = KEYWORDS.indexOf(id) >= 0 ? id.toUpperCase() : 'IDENTIFIER';
|
||||
if (tag === 'WHEN' && (this.tag() === 'OUTDENT' || this.tag() === 'INDENT')) {
|
||||
tag = 'LEADING_WHEN';
|
||||
}
|
||||
if (tag === 'IDENTIFIER' && this.value() === '::') {
|
||||
this.tag(-1, 'PROTOTYPE_ACCESS');
|
||||
}
|
||||
if (tag === 'IDENTIFIER' && this.value() === '.' && !(this.value(-2) === '.')) {
|
||||
if (this.tag(-2) === '?') {
|
||||
this.tag(-1, 'SOAK_ACCESS');
|
||||
this.tokens.splice(-2, 1);
|
||||
} else {
|
||||
this.tag(-1, 'PROPERTY_ACCESS');
|
||||
}
|
||||
}
|
||||
this.token(tag, id);
|
||||
this.i += id.length;
|
||||
return true;
|
||||
};
|
||||
// Matches numbers, including decimals, hex, and exponential notation.
|
||||
lex.prototype.number_token = function number_token() {
|
||||
var number;
|
||||
if (!((number = this.match(NUMBER, 1)))) {
|
||||
return false;
|
||||
}
|
||||
this.token('NUMBER', number);
|
||||
this.i += number.length;
|
||||
return true;
|
||||
};
|
||||
// Matches strings, including multi-line strings.
|
||||
lex.prototype.string_token = function string_token() {
|
||||
var escaped, string;
|
||||
if (!((string = this.match(STRING, 1)))) {
|
||||
return false;
|
||||
}
|
||||
escaped = string.replace(STRING_NEWLINES, " \\\n");
|
||||
this.token('STRING', escaped);
|
||||
this.line += this.count(string, "\n");
|
||||
this.i += string.length;
|
||||
return true;
|
||||
};
|
||||
// Matches heredocs, adjusting indentation to the correct level.
|
||||
lex.prototype.heredoc_token = function heredoc_token() {
|
||||
var doc, indent, match;
|
||||
if (!((match = this.chunk.match(HEREDOC)))) {
|
||||
return false;
|
||||
}
|
||||
doc = match[2] || match[4];
|
||||
indent = doc.match(HEREDOC_INDENT).sort()[0];
|
||||
doc = doc.replace(new RegExp("^" + indent, 'g'), '').replace(MULTILINER, "\\n").replace('"', '\\"');
|
||||
this.token('STRING', '"' + doc + '"');
|
||||
this.line += this.count(match[1], "\n");
|
||||
this.i += match[1].length;
|
||||
return true;
|
||||
};
|
||||
// Matches interpolated JavaScript.
|
||||
lex.prototype.js_token = function js_token() {
|
||||
var script;
|
||||
if (!((script = this.match(JS, 1)))) {
|
||||
return false;
|
||||
}
|
||||
this.token('JS', script.replace(JS_CLEANER, ''));
|
||||
this.i += script.length;
|
||||
return true;
|
||||
};
|
||||
// Matches regular expression literals.
|
||||
lex.prototype.regex_token = function regex_token() {
|
||||
var regex;
|
||||
if (!((regex = this.match(REGEX, 1)))) {
|
||||
return false;
|
||||
}
|
||||
if (NOT_REGEX.indexOf(this.tag()) >= 0) {
|
||||
return false;
|
||||
}
|
||||
this.token('REGEX', regex);
|
||||
this.i += regex.length;
|
||||
return true;
|
||||
};
|
||||
// Matches and conumes comments.
|
||||
lex.prototype.comment_token = function comment_token() {
|
||||
var comment;
|
||||
if (!((comment = this.match(COMMENT, 1)))) {
|
||||
return false;
|
||||
}
|
||||
this.line += comment.match(MULTILINER).length;
|
||||
this.token('COMMENT', comment.replace(COMMENT_CLEANER, '').split(MULTILINER));
|
||||
this.token('TERMINATOR', "\n");
|
||||
this.i += comment.length;
|
||||
return true;
|
||||
};
|
||||
// Record tokens for indentation differing from the previous line.
|
||||
lex.prototype.indent_token = function indent_token() {
|
||||
var diff, indent, next_character, no_newlines, size;
|
||||
if (!((indent = this.match(MULTI_DENT, 1)))) {
|
||||
return false;
|
||||
}
|
||||
this.line += indent.match(MULTILINER).length;
|
||||
this.i += indent.length;
|
||||
next_character = this.chunk.match(MULTI_DENT)[4];
|
||||
no_newlines = next_character === '.' || (this.value().match(NO_NEWLINE) && this.tokens[this.tokens.length - 2][0] !== '.' && !this.value().match(CODE));
|
||||
if (no_newlines) {
|
||||
return this.suppress_newlines(indent);
|
||||
}
|
||||
size = indent.match(LAST_DENTS).reverse()[0].match(LAST_DENT)[1].length;
|
||||
if (size === this.indent) {
|
||||
return this.newline_token(indent);
|
||||
}
|
||||
if (size > this.indent) {
|
||||
diff = size - this.indent;
|
||||
this.token('INDENT', diff);
|
||||
this.indents.push(diff);
|
||||
} else {
|
||||
this.outdent_token(this.indent - size);
|
||||
}
|
||||
this.indent = size;
|
||||
return true;
|
||||
};
|
||||
// Record an oudent token or tokens, if we're moving back inwards past
|
||||
// multiple recorded indents.
|
||||
lex.prototype.outdent_token = function outdent_token(move_out) {
|
||||
var last_indent;
|
||||
while (move_out > 0 && this.indents.length) {
|
||||
last_indent = this.indents.pop();
|
||||
this.token('OUTDENT', last_indent);
|
||||
move_out -= last_indent;
|
||||
}
|
||||
this.token('TERMINATOR', "\n");
|
||||
return true;
|
||||
};
|
||||
// Matches and consumes non-meaningful whitespace.
|
||||
lex.prototype.whitespace_token = function whitespace_token() {
|
||||
var space;
|
||||
if (!((space = this.match(WHITESPACE, 1)))) {
|
||||
return false;
|
||||
}
|
||||
this.spaced = this.value();
|
||||
this.i += space.length;
|
||||
return true;
|
||||
};
|
||||
// Multiple newlines get merged together.
|
||||
// Use a trailing \ to escape newlines.
|
||||
lex.prototype.newline_token = function newline_token(newlines) {
|
||||
if (!(this.value() === "\n")) {
|
||||
this.token('TERMINATOR', "\n");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// Tokens to explicitly escape newlines are removed once their job is done.
|
||||
lex.prototype.suppress_newlines = function suppress_newlines(newlines) {
|
||||
if (this.value() === "\\") {
|
||||
this.tokens.pop();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// 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.
|
||||
lex.prototype.literal_token = function literal_token() {
|
||||
var match, tag, value;
|
||||
match = this.chunk.match(OPERATOR);
|
||||
value = match && match[1];
|
||||
if (value && value.match(CODE)) {
|
||||
this.tag_parameters();
|
||||
}
|
||||
value = value || this.chunk.substr(0, 1);
|
||||
tag = value.match(ASSIGNMENT) ? 'ASSIGN' : value;
|
||||
if (value === ';') {
|
||||
tag = 'TERMINATOR';
|
||||
}
|
||||
if (this.value() !== this.spaced && CALLABLE.indexOf(this.tag()) >= 0) {
|
||||
if (value === '(') {
|
||||
tag = 'CALL_START';
|
||||
}
|
||||
if (value === '[') {
|
||||
tag = 'INDEX_START';
|
||||
}
|
||||
}
|
||||
this.token(tag, value);
|
||||
this.i += value.length;
|
||||
return true;
|
||||
};
|
||||
// Helpers =============================================================
|
||||
// Add a token to the results, taking note of the line number.
|
||||
lex.prototype.token = function token(tag, value) {
|
||||
return this.tokens.push([tag, value]);
|
||||
// this.tokens.push([tag, Value.new(value, @line)])
|
||||
};
|
||||
// Look at a tag in the current token stream.
|
||||
lex.prototype.tag = function tag(index, tag) {
|
||||
var tok;
|
||||
if (!((tok = this.tokens[this.tokens.length - (index || 1)]))) {
|
||||
return null;
|
||||
}
|
||||
if ((typeof tag !== "undefined" && tag !== null)) {
|
||||
return (tok[0] = tag);
|
||||
}
|
||||
return tok[0];
|
||||
};
|
||||
// Look at a value in the current token stream.
|
||||
lex.prototype.value = function value(index, val) {
|
||||
var tok;
|
||||
if (!((tok = this.tokens[this.tokens.length - (index || 1)]))) {
|
||||
return null;
|
||||
}
|
||||
if ((typeof val !== "undefined" && val !== null)) {
|
||||
return (tok[1] = val);
|
||||
}
|
||||
return tok[1];
|
||||
};
|
||||
// Count the occurences of a character in a string.
|
||||
lex.prototype.count = function count(string, letter) {
|
||||
var num, pos;
|
||||
num = 0;
|
||||
pos = string.indexOf(letter);
|
||||
while (pos !== -1) {
|
||||
count += 1;
|
||||
pos = string.indexOf(letter, pos + 1);
|
||||
}
|
||||
return count;
|
||||
};
|
||||
// Attempt to match a string against the current chunk, returning the indexed
|
||||
// match.
|
||||
lex.prototype.match = function match(regex, index) {
|
||||
var m;
|
||||
if (!((m = this.chunk.match(regex)))) {
|
||||
return false;
|
||||
}
|
||||
return m ? m[index] : false;
|
||||
};
|
||||
// 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.
|
||||
lex.prototype.tag_parameters = function tag_parameters() {
|
||||
var i, tok;
|
||||
if (this.tag() !== ')') {
|
||||
return null;
|
||||
}
|
||||
i = 0;
|
||||
while (true) {
|
||||
i += 1;
|
||||
tok = this.tokens[this.tokens.length - i];
|
||||
if (!tok) {
|
||||
return null;
|
||||
}
|
||||
if (tok[0] === 'IDENTIFIER') {
|
||||
tok[0] = 'PARAM';
|
||||
} else if (tok[0] === ')') {
|
||||
tok[0] = 'PARAM_END';
|
||||
} else if (tok[0] === '(') {
|
||||
return (tok[0] = 'PARAM_START');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// Close up all remaining open blocks. IF the first token is an indent,
|
||||
// axe it.
|
||||
lex.prototype.close_indentation = function close_indentation() {
|
||||
return this.outdent_token(this.indent);
|
||||
};
|
||||
})();
|
||||
@@ -12,33 +12,30 @@ module CoffeeScript
|
||||
"new", "return",
|
||||
"try", "catch", "finally", "throw",
|
||||
"break", "continue",
|
||||
"for", "in", "of", "by", "where", "while",
|
||||
"delete", "instanceof", "typeof",
|
||||
"for", "in", "by", "where", "while",
|
||||
"switch", "when",
|
||||
"super", "extends"]
|
||||
"super", "extends",
|
||||
"delete", "instanceof", "typeof"]
|
||||
|
||||
# Token matching regexes.
|
||||
IDENTIFIER = /\A([a-zA-Z$_](\w|\$)*)/
|
||||
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?(.*?)\n?([ \t]*)"{3}|'{3}\n?(.*?)\n?([ \t]*)'{3})/m
|
||||
JS = /\A(``|`(.*?)([^\\]|\\\\)`)/m
|
||||
OPERATOR = /\A([+\*&|\/\-%=<>:!?]+)/
|
||||
STRING = /\A(""|''|"(.*?)[^\\]"|'(.*?)[^\\]')/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]*))+)(\.)?/
|
||||
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/
|
||||
STRING_NEWLINES = /\n[ \t]*/
|
||||
COMMENT_CLEANER = /(^[ \t]*#|\n[ \t]*$)/
|
||||
NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/
|
||||
HEREDOC_INDENT = /^[ \t]+/
|
||||
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.
|
||||
@@ -46,21 +43,17 @@ module CoffeeScript
|
||||
NOT_REGEX = [
|
||||
:IDENTIFIER, :NUMBER, :REGEX, :STRING,
|
||||
')', '++', '--', ']', '}',
|
||||
:FALSE, :NULL, :TRUE
|
||||
:FALSE, :NULL, :THIS, :TRUE
|
||||
]
|
||||
|
||||
# Tokens which could legitimately be invoked or indexed.
|
||||
CALLABLE = [:IDENTIFIER, :SUPER, ')', ']', '}', :STRING]
|
||||
|
||||
# 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]
|
||||
@spaced = nil # The last value that has a space following it.
|
||||
@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
|
||||
@@ -75,7 +68,6 @@ module CoffeeScript
|
||||
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
|
||||
@@ -94,15 +86,7 @@ module CoffeeScript
|
||||
# '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] = :PROTOTYPE_ACCESS if tag == :IDENTIFIER && last_value == '::'
|
||||
if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2] && @tokens[-2][1] == '.')
|
||||
if @tokens[-2][0] == "?"
|
||||
@tokens[-1][0] = :SOAK_ACCESS
|
||||
@tokens.delete_at(-2)
|
||||
else
|
||||
@tokens[-1][0] = :PROPERTY_ACCESS
|
||||
end
|
||||
end
|
||||
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2][1] == '.')
|
||||
token(tag, identifier)
|
||||
@i += identifier.length
|
||||
end
|
||||
@@ -117,25 +101,14 @@ module CoffeeScript
|
||||
# Matches strings, including multi-line strings.
|
||||
def string_token
|
||||
return false unless string = @chunk[STRING, 1]
|
||||
escaped = string.gsub(STRING_NEWLINES, " \\\n")
|
||||
escaped = string.gsub(MULTILINER) do |match|
|
||||
@line += 1
|
||||
" \\\n"
|
||||
end
|
||||
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)
|
||||
doc = match[2] || match[4]
|
||||
indent = doc.scan(HEREDOC_INDENT).min
|
||||
doc.gsub!(/^#{indent}/, "")
|
||||
doc.gsub!("\n", "\\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]
|
||||
@@ -165,9 +138,7 @@ module CoffeeScript
|
||||
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) && @tokens[-2][0] != '.' && !last_value.match(CODE))
|
||||
return suppress_newlines(indent) if no_newlines
|
||||
return suppress_newlines(indent) if last_value.to_s.match(NO_NEWLINE) && last_value != "=>"
|
||||
size = indent.scan(LAST_DENT).last.last.length
|
||||
return newline_token(indent) if size == @indent
|
||||
if size > @indent
|
||||
@@ -193,7 +164,6 @@ module CoffeeScript
|
||||
# Matches and consumes non-meaningful whitespace.
|
||||
def whitespace_token
|
||||
return false unless whitespace = @chunk[WHITESPACE, 1]
|
||||
@spaced = last_value
|
||||
@i += whitespace.length
|
||||
end
|
||||
|
||||
@@ -218,17 +188,14 @@ module CoffeeScript
|
||||
tag_parameters if value && value.match(CODE)
|
||||
value ||= @chunk[0,1]
|
||||
tag = value.match(ASSIGNMENT) ? :ASSIGN : value
|
||||
if !@spaced.equal?(last_value) && CALLABLE.include?(last_tag)
|
||||
tag = :CALL_START if value == '('
|
||||
tag = :INDEX_START if value == '['
|
||||
end
|
||||
token(tag, value)
|
||||
@i += value.length
|
||||
end
|
||||
|
||||
# Helpers ==========================================================
|
||||
|
||||
# Add a token to the results, taking note of the line number.
|
||||
# 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
|
||||
@@ -248,17 +215,15 @@ module CoffeeScript
|
||||
# parameter identifiers in order to avoid this. Also, parameter lists can
|
||||
# make use of splats.
|
||||
def tag_parameters
|
||||
return if last_tag != ')'
|
||||
i = 0
|
||||
loop do
|
||||
i -= 1
|
||||
tok = @tokens[i]
|
||||
return if !tok
|
||||
case tok[0]
|
||||
when :IDENTIFIER then tok[0] = :PARAM
|
||||
when ')' then tok[0] = :PARAM_END
|
||||
when '(' then return tok[0] = :PARAM_START
|
||||
end
|
||||
next if tok[0] == ','
|
||||
next tok[0] = :PARAM_SPLAT if tok[0] == '*'
|
||||
return if tok[0] != :IDENTIFIER
|
||||
tok[0] = :PARAM
|
||||
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())
|
||||
print(result) if result isnt undefined
|
||||
catch e
|
||||
print(e)
|
||||
|
||||
# Compile a given CoffeeScript file into JavaScript.
|
||||
exports.compileFile: path =>
|
||||
coffee: OS.popen([coffeePath, "--print", "--no-wrap", path])
|
||||
checkForErrors(coffee)
|
||||
coffee.stdout.read()
|
||||
|
||||
# Compile a string of CoffeeScript into JavaScript.
|
||||
exports.compile: source =>
|
||||
coffee: OS.popen([coffeePath, "--eval", "--no-wrap"])
|
||||
coffee.stdin.write(source).flush().close()
|
||||
checkForErrors(coffee)
|
||||
coffee.stdout.read()
|
||||
|
||||
# Evaluating a string of CoffeeScript first compiles it externally.
|
||||
exports.evalCS: source =>
|
||||
eval(exports.compile(source))
|
||||
|
||||
# Make a factory for the CoffeeScript environment.
|
||||
exports.makeNarwhalFactory: path =>
|
||||
code: exports.compileFile(path)
|
||||
factoryText: "function(require,exports,module,system,print){" + code + "/**/\n}"
|
||||
if system.engine is "rhino"
|
||||
Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null)
|
||||
else
|
||||
# eval requires parentheses, but parentheses break compileFunction.
|
||||
eval("(" + factoryText + ")")
|
||||
@@ -1,12 +1,14 @@
|
||||
(function(){
|
||||
var File, OS, Readline, checkForErrors, coffeePath, factories, loader, puts;
|
||||
// The Narwhal-compatibility wrapper for CoffeeScript.
|
||||
var File, OS, Readline, checkForErrors, coffeePath;
|
||||
// This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee
|
||||
// Executes the `coffee` Ruby program to convert from CoffeeScript
|
||||
// to Javascript. Eventually this will hopefully happen entirely within JS.
|
||||
// Require external dependencies.
|
||||
OS = require('os');
|
||||
File = require('file');
|
||||
Readline = require('readline');
|
||||
// The path to the CoffeeScript Compiler.
|
||||
coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().join('bin', 'coffee');
|
||||
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) {
|
||||
@@ -15,36 +17,35 @@
|
||||
system.stderr.print(coffeeProcess.stderr.read());
|
||||
throw new Error("CoffeeScript compile error");
|
||||
};
|
||||
// Alias print to "puts", for Node.js compatibility:
|
||||
puts = print;
|
||||
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every
|
||||
// command.
|
||||
exports.run = function run(args) {
|
||||
var __a, __b, i, path, result;
|
||||
var __a, __b, __c, 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];
|
||||
__b = [];
|
||||
for (i in __a) {
|
||||
if (__a.hasOwnProperty(i)) {
|
||||
path = __a[i];
|
||||
exports.evalCS(File.read(path));
|
||||
__c = delete args[i];
|
||||
__b.push(__c);
|
||||
}
|
||||
}
|
||||
__b;
|
||||
return true;
|
||||
}
|
||||
__b = [];
|
||||
while (true) {
|
||||
__b.push((function() {
|
||||
try {
|
||||
system.stdout.write('coffee> ').flush();
|
||||
result = exports.evalCS(Readline.readline(), ['--globals']);
|
||||
if (result !== undefined) {
|
||||
return print(result);
|
||||
}
|
||||
} catch (e) {
|
||||
return print(e);
|
||||
try {
|
||||
system.stdout.write('coffee> ').flush();
|
||||
result = exports.evalCS(Readline.readline());
|
||||
if (result !== undefined) {
|
||||
print(result);
|
||||
}
|
||||
}).call(this));
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
return __b;
|
||||
};
|
||||
// Compile a given CoffeeScript file into JavaScript.
|
||||
exports.compileFile = function compileFile(path) {
|
||||
@@ -54,16 +55,16 @@
|
||||
return coffee.stdout.read();
|
||||
};
|
||||
// Compile a string of CoffeeScript into JavaScript.
|
||||
exports.compile = function compile(source, flags) {
|
||||
exports.compile = function compile(source) {
|
||||
var coffee;
|
||||
coffee = OS.popen([coffeePath, "--eval", "--no-wrap"].concat(flags || []));
|
||||
coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
|
||||
coffee.stdin.write(source).flush().close();
|
||||
checkForErrors(coffee);
|
||||
return coffee.stdout.read();
|
||||
};
|
||||
// Evaluating a string of CoffeeScript first compiles it externally.
|
||||
exports.evalCS = function evalCS(source, flags) {
|
||||
return eval(exports.compile(source, flags));
|
||||
exports.evalCS = function evalCS(source) {
|
||||
return eval(exports.compile(source));
|
||||
};
|
||||
// Make a factory for the CoffeeScript environment.
|
||||
exports.makeNarwhalFactory = function makeNarwhalFactory(path) {
|
||||
@@ -77,20 +78,4 @@
|
||||
return eval("(" + factoryText + ")");
|
||||
}
|
||||
};
|
||||
// The Narwhal loader for '.coffee' files.
|
||||
factories = {
|
||||
};
|
||||
loader = {
|
||||
};
|
||||
// Reload the coffee-script environment from source.
|
||||
loader.reload = function reload(topId, path) {
|
||||
return factories[topId] = function() {
|
||||
return exports.makeNarwhalFactory(path);
|
||||
};
|
||||
};
|
||||
// Ensure that the coffee-script environment is loaded.
|
||||
loader.load = function load(topId, path) {
|
||||
return factories[topId] = factories[topId] || this.reload(topId, path);
|
||||
};
|
||||
require.loader.loaders.unshift([".coffee", loader]);
|
||||
})();
|
||||
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])
|
||||
@@ -1,443 +0,0 @@
|
||||
(function(){
|
||||
var compact, dup, flatten;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
// 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.
|
||||
exports.Node = function Node() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.values = arguments;
|
||||
__a = this.name = this.constructor.name;
|
||||
return Node === this.constructor ? this : __a;
|
||||
};
|
||||
exports.Expressions = function Expressions() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return Expressions === this.constructor ? this : __a;
|
||||
};
|
||||
exports.LiteralNode = function LiteralNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return LiteralNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ReturnNode = function ReturnNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ReturnNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.CommentNode = function CommentNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return CommentNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.CallNode = function CallNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return CallNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ExtendsNode = function ExtendsNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ExtendsNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ValueNode = function ValueNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ValueNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.AccessorNode = function AccessorNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return AccessorNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.IndexNode = function IndexNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return IndexNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.RangeNode = function RangeNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return RangeNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.SliceNode = function SliceNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return SliceNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.AssignNode = function AssignNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return AssignNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.OpNode = function OpNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return OpNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.CodeNode = function CodeNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return CodeNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.SplatNode = function SplatNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return SplatNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ObjectNode = function ObjectNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ObjectNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ArrayNode = function ArrayNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ArrayNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.PushNode = function PushNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return PushNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ClosureNode = function ClosureNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ClosureNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.WhileNode = function WhileNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return WhileNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ForNode = function ForNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ForNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.TryNode = function TryNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return TryNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ThrowNode = function ThrowNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ThrowNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ExistenceNode = function ExistenceNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ExistenceNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.ParentheticalNode = function ParentheticalNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return ParentheticalNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.IfNode = function IfNode() {
|
||||
var __a;
|
||||
var arguments = Array.prototype.slice.call(arguments, 0);
|
||||
this.name = this.constructor.name;
|
||||
__a = this.values = arguments;
|
||||
return IfNode === this.constructor ? this : __a;
|
||||
};
|
||||
exports.Expressions.wrap = function wrap(values) {
|
||||
return this.values = values;
|
||||
};
|
||||
// Some helper functions
|
||||
// TODO -- shallow (1 deep) flatten..
|
||||
// need recursive version..
|
||||
flatten = function flatten(aggList, newList) {
|
||||
var __a, __b, item;
|
||||
__a = newList;
|
||||
for (__b = 0; __b < __a.length; __b++) {
|
||||
item = __a[__b];
|
||||
aggList.push(item);
|
||||
}
|
||||
return aggList;
|
||||
};
|
||||
compact = function compact(input) {
|
||||
var __a, __b, __c, compected, item;
|
||||
compected = [];
|
||||
__a = []; __b = input;
|
||||
for (__c = 0; __c < __b.length; __c++) {
|
||||
item = __b[__c];
|
||||
__a.push((typeof item !== "undefined" && item !== null) ? compacted.push(item) : null);
|
||||
}
|
||||
return __a;
|
||||
};
|
||||
dup = function dup(input) {
|
||||
var __a, __b, __c, key, output, val;
|
||||
output = null;
|
||||
if (input instanceof Array) {
|
||||
output = [];
|
||||
__a = input;
|
||||
for (__b = 0; __b < __a.length; __b++) {
|
||||
val = __a[__b];
|
||||
output.push(val);
|
||||
}
|
||||
} else {
|
||||
output = {
|
||||
};
|
||||
__c = input;
|
||||
for (key in __c) {
|
||||
val = __c[key];
|
||||
if (__hasProp.call(__c, key)) {
|
||||
output.key = val;
|
||||
}
|
||||
}
|
||||
output;
|
||||
}
|
||||
return output;
|
||||
};
|
||||
exports.Node.prototype.TAB = ' ';
|
||||
// Tag this node as a statement, meaning that it can't be used directly as
|
||||
// the result of an expression.
|
||||
exports.Node.prototype.mark_as_statement = function mark_as_statement() {
|
||||
return this.is_statement = function is_statement() {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
// 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.
|
||||
exports.Node.prototype.mark_as_statement_only = function mark_as_statement_only() {
|
||||
this.mark_as_statement();
|
||||
return this.is_statement_only = function is_statement_only() {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
// This node needs to know if it's being compiled as a top-level statement,
|
||||
// in order to compile without special expression conversion.
|
||||
exports.Node.prototype.mark_as_top_sensitive = function mark_as_top_sensitive() {
|
||||
return this.is_top_sensitive = function is_top_sensitive() {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
// Provide a quick implementation of a children method.
|
||||
exports.Node.prototype.children = function children(attributes) {
|
||||
var __a, __b, agg, compacted, item;
|
||||
// TODO -- are these optimal impls of flatten and compact
|
||||
// .. do better ones exist in a stdlib?
|
||||
agg = [];
|
||||
__a = attributes;
|
||||
for (__b = 0; __b < __a.length; __b++) {
|
||||
item = __a[__b];
|
||||
agg = flatten(agg, item);
|
||||
}
|
||||
compacted = compact(agg);
|
||||
return this.children = function children() {
|
||||
return compacted;
|
||||
};
|
||||
};
|
||||
exports.Node.prototype.write = function write(code) {
|
||||
// hm..
|
||||
// TODO -- should print to STDOUT in "VERBOSE" how to
|
||||
// go about this.. ? jsonify 'this'?
|
||||
// use node's puts ??
|
||||
return code;
|
||||
};
|
||||
// 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.
|
||||
exports.Node.prototype.compile = function compile(o) {
|
||||
var closure, opts, top;
|
||||
// TODO -- need JS dup/clone
|
||||
opts = (typeof !o !== "undefined" && !o !== null) ? {
|
||||
} : o;
|
||||
this.options = opts;
|
||||
this.indent = opts.indent;
|
||||
top = this.options.top;
|
||||
!this.is_top_sentitive() ? (this.options.top = undefined) : null;
|
||||
closure = this.is_statement() && !this.is_statement_only() && !top && typeof (this) === "CommentNode";
|
||||
closure = closure && !this.do_i_contain(function(n) {
|
||||
return n.is_statement_only();
|
||||
});
|
||||
return closure ? this.compile_closure(this.options) : compile_node(this.options);
|
||||
};
|
||||
// Statements converted into expressions share scope with their parent
|
||||
// closure, to preserve JavaScript-style lexical scope.
|
||||
exports.Node.prototype.compile_closure = function compile_closure(o) {
|
||||
var opts;
|
||||
opts = (typeof !o !== "undefined" && !o !== null) ? {
|
||||
} : o;
|
||||
this.indent = opts.indent;
|
||||
opts.shared_scope = o.scope;
|
||||
return exports.ClosureNode.wrap(this).compile(opts);
|
||||
};
|
||||
// Quick short method for the current indentation level, plus tabbing in.
|
||||
exports.Node.prototype.idt = function idt(tLvl) {
|
||||
var __a, __b, __c, __d, tabAmt, tabs, x;
|
||||
tabs = (typeof tLvl !== "undefined" && tLvl !== null) ? tLvl : 0;
|
||||
tabAmt = '';
|
||||
__c = 0; __d = tabs;
|
||||
for (__b=0, x=__c; (__c <= __d ? x < __d : x > __d); (__c <= __d ? x += 1 : x -= 1), __b++) {
|
||||
tabAmt = tabAmt + this.TAB;
|
||||
}
|
||||
return this.indent + tabAmt;
|
||||
};
|
||||
//Does this node, or any of it's children, contain a node of a certain kind?
|
||||
exports.Node.prototype.do_i_contain = function do_i_contain(block) {
|
||||
var __a, __b, node;
|
||||
__a = this.children;
|
||||
for (__b = 0; __b < __a.length; __b++) {
|
||||
node = __a[__b];
|
||||
if (block(node)) {
|
||||
return true;
|
||||
}
|
||||
if (node instanceof exports.Node && node.do_i_contain(block)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
// Default implementations of the common node methods.
|
||||
exports.Node.prototype.unwrap = function unwrap() {
|
||||
return this;
|
||||
};
|
||||
exports.Node.prototype.children = [];
|
||||
exports.Node.prototype.is_a_statement = function is_a_statement() {
|
||||
return false;
|
||||
};
|
||||
exports.Node.prototype.is_a_statement_only = function is_a_statement_only() {
|
||||
return false;
|
||||
};
|
||||
exports.Node.prototype.is_top_sensitive = function is_top_sensitive() {
|
||||
return false;
|
||||
};
|
||||
// A collection of nodes, each one representing an expression.
|
||||
// exports.Expressions: (nodes) ->
|
||||
// this.mark_as_statement()
|
||||
// this.expressions: []
|
||||
// this.children([this.expressions])
|
||||
// for n in nodes
|
||||
// this.expressions: flatten this.expressions, n
|
||||
// exports.Expressions extends exports.Node
|
||||
exports.Expressions.prototype.TRAILING_WHITESPACE = /\s+$/;
|
||||
// Wrap up a node as an Expressions, unless it already is.
|
||||
exports.Expressions.prototype.wrap = function wrap(nodes) {
|
||||
if (nodes.length === 1 && nodes[0] instanceof exports.Expressions) {
|
||||
return nodes[0];
|
||||
}
|
||||
return new Expressions(nodes);
|
||||
};
|
||||
// Tack an expression on to the end of this expression list.
|
||||
exports.Expressions.prototype.push = function push(node) {
|
||||
this.expressions.push(node);
|
||||
return this;
|
||||
};
|
||||
// Tack an expression on to the beginning of this expression list.
|
||||
exports.Expressions.prototype.unshift = function unshift(node) {
|
||||
this.expressions.unshift(node);
|
||||
return this;
|
||||
};
|
||||
// If this Expressions consists of a single node, pull it back out.
|
||||
exports.Expressions.prototype.unwrap = function unwrap() {
|
||||
return this.expressions.length === 1 ? this.expressions[0] : this;
|
||||
};
|
||||
// Is this an empty block of code?
|
||||
exports.Expressions.prototype.is_empty = function is_empty() {
|
||||
return this.expressions.length === 0;
|
||||
};
|
||||
// Is the node last in this block of expressions.
|
||||
exports.Expressions.prototype.is_last = function is_last(node) {
|
||||
var arr_length;
|
||||
arr_length = this.expressions.length;
|
||||
this.last_index = this.last_index || this.expressions[arr_length - 1] instanceof exports.CommentNode ? -2 : -1;
|
||||
return node === this.expressions[arr_length - this.last_index];
|
||||
};
|
||||
exports.Expressions.prototype.compile = function compile(o) {
|
||||
var opts;
|
||||
opts = (typeof o !== "undefined" && o !== null) ? o : {
|
||||
};
|
||||
return opts.scope ? exports.Expressions.__superClass__.compile.call(this, dup(opts)) : this.compile_root(o);
|
||||
};
|
||||
// Compile each expression in the Expressions body.
|
||||
exports.Expressions.prototype.compile_node = function compile_node(options) {
|
||||
var __a, __b, __c, __d, __e, code, compiled, e, line, opts;
|
||||
opts = (typeof options !== "undefined" && options !== null) ? options : {
|
||||
};
|
||||
compiled = [];
|
||||
__a = this.expressions;
|
||||
for (__b = 0; __b < __a.length; __b++) {
|
||||
e = __a[__b];
|
||||
compiled.push(this.compile_expression(e, dup(options)));
|
||||
}
|
||||
code = '';
|
||||
__c = []; __d = compiled;
|
||||
for (__e = 0; __e < __d.length; __e++) {
|
||||
line = __d[__e];
|
||||
__c.push((code = code + line + '\n'));
|
||||
}
|
||||
return __c;
|
||||
};
|
||||
// If this is the top-level Expressions, wrap everything in a safety closure.
|
||||
exports.Expressions.prototype.compile_root = function compile_root(o) {
|
||||
var code, indent, opts;
|
||||
opts = (typeof o !== "undefined" && o !== null) ? o : {
|
||||
};
|
||||
indent = opts.no_wrap ? '' : this.TAB;
|
||||
this.indent = indent;
|
||||
opts.indent = indent;
|
||||
opts.scope = new Scope(null, this, null);
|
||||
code = opts.globals ? compile_node(opts) : compile_with_declarations(opts);
|
||||
code.replace(this.TRAILING_WHITESPACE, '');
|
||||
return this.write(opts.no_wrap ? code : "(function(){\n" + code + "\n})();");
|
||||
};
|
||||
})();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,22 +5,16 @@ module CoffeeScript
|
||||
# line-number aware.
|
||||
class ParseError < Racc::ParseError
|
||||
|
||||
TOKEN_MAP = {
|
||||
'INDENT' => 'indent',
|
||||
'OUTDENT' => 'outdent',
|
||||
"\n" => 'newline'
|
||||
}
|
||||
|
||||
def initialize(token_id, value, stack=nil, message=nil)
|
||||
@token_id, @value, @stack, @message = token_id, value, stack, message
|
||||
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.to_s ? " unexpected #{@token_id.to_s.downcase}" : ""
|
||||
val_part = @message || "for #{TOKEN_MAP[@value.to_s] || "'#{@value}'"}"
|
||||
"#{line_part} syntax error, #{val_part}#{id_part}"
|
||||
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
|
||||
|
||||
|
||||
@@ -1,477 +0,0 @@
|
||||
(function(){
|
||||
var Parser, __a, __b, __c, __d, __e, __f, bnf, grammar, name, non_terminal, o, operators, option, parser, part, tokens, unwrap;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
Parser = require('jison').Parser;
|
||||
process.mixin(require('./nodes'));
|
||||
// DSL ===================================================================
|
||||
// Detect functions: [
|
||||
unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/;
|
||||
// Quickie DSL for Jison access.
|
||||
o = function o(pattern_string, func) {
|
||||
var match;
|
||||
if (func) {
|
||||
func = (match = (func + "").match(unwrap)) ? match[1] : '(' + func + '())';
|
||||
return [pattern_string, '$$ = ' + func + ';'];
|
||||
} else {
|
||||
return [pattern_string, '$$ = $1;'];
|
||||
}
|
||||
};
|
||||
// Precedence ===========================================================
|
||||
operators = [["left", '?'], ["right", '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", '||=', '&&=', '?='], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'IF', 'ELSE', 'WHILE']];
|
||||
// Grammar ==============================================================
|
||||
grammar = {
|
||||
// All parsing will end in this rule, being the trunk of the AST.
|
||||
Root: [o("", function() {
|
||||
return new Expressions();
|
||||
}), o("TERMINATOR", function() {
|
||||
return new Expressions();
|
||||
}), o("Expressions"), o("Block TERMINATOR")
|
||||
],
|
||||
// Any list of expressions or method body, seperated by line breaks or semis.
|
||||
Expressions: [o("Expression", function() {
|
||||
return Expressions.wrap([$1]);
|
||||
}), o("Expressions TERMINATOR Expression", function() {
|
||||
return $1.push($3);
|
||||
}), o("Expressions TERMINATOR")
|
||||
],
|
||||
// All types of expressions in our language. The basic unit of CoffeeScript
|
||||
// is the expression.
|
||||
Expression: [o("Value"), o("Call"), o("Code"), o("Operation"), o("Assign"), o("If"), o("Try"), o("Throw"), o("Return"), o("While"), o("For"), o("Switch"), o("Extends"), o("Splat"), o("Existence"), o("Comment")],
|
||||
// A block of expressions. Note that the Rewriter will convert some postfix
|
||||
// forms into blocks for us, by altering the token stream.
|
||||
Block: [o("INDENT Expressions OUTDENT", function() {
|
||||
return $2;
|
||||
}), o("INDENT OUTDENT", function() {
|
||||
return new Expressions();
|
||||
})
|
||||
],
|
||||
// All hard-coded values. These can be printed straight to JavaScript.
|
||||
Literal: [o("NUMBER", function() {
|
||||
return new LiteralNode(yytext);
|
||||
}), o("STRING", function() {
|
||||
return new LiteralNode(yytext);
|
||||
}), o("JS", function() {
|
||||
return new LiteralNode(yytext);
|
||||
}), o("REGEX", function() {
|
||||
return new LiteralNode(yytext);
|
||||
}), o("BREAK", function() {
|
||||
return new LiteralNode(yytext);
|
||||
}), o("CONTINUE", function() {
|
||||
return new LiteralNode(yytext);
|
||||
}), o("ARGUMENTS", function() {
|
||||
return new LiteralNode(yytext);
|
||||
}), o("TRUE", function() {
|
||||
return new LiteralNode(true);
|
||||
}), o("FALSE", function() {
|
||||
return new LiteralNode(false);
|
||||
}), o("YES", function() {
|
||||
return new LiteralNode(true);
|
||||
}), o("NO", function() {
|
||||
return new LiteralNode(false);
|
||||
}), o("ON", function() {
|
||||
return new LiteralNode(true);
|
||||
}), o("OFF", function() {
|
||||
return new LiteralNode(false);
|
||||
})
|
||||
],
|
||||
// Assignment to a variable (or index).
|
||||
Assign: [o("Value ASSIGN Expression", function() {
|
||||
return new AssignNode($1, $3);
|
||||
})
|
||||
],
|
||||
// Assignment within an object literal (can be quoted).
|
||||
AssignObj: [o("IDENTIFIER ASSIGN Expression", function() {
|
||||
return new AssignNode(new ValueNode(yytext), $3, 'object');
|
||||
}), o("STRING ASSIGN Expression", function() {
|
||||
return new AssignNode(new ValueNode(new LiteralNode(yytext)), $3, 'object');
|
||||
}), o("Comment")
|
||||
],
|
||||
// A return statement.
|
||||
Return: [o("RETURN Expression", function() {
|
||||
return new ReturnNode($2);
|
||||
}), o("RETURN", function() {
|
||||
return new ReturnNode(new ValueNode(new LiteralNode('null')));
|
||||
})
|
||||
],
|
||||
// A comment.
|
||||
Comment: [o("COMMENT", function() {
|
||||
return new CommentNode(yytext);
|
||||
})
|
||||
],
|
||||
//
|
||||
// # Arithmetic and logical operators
|
||||
// # For Ruby's Operator precedence, see: [
|
||||
// # https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
|
||||
// Operation: [
|
||||
// o "! Expression", -> new OpNode($1, $2)
|
||||
// o "!! Expression", -> new OpNode($1, $2)
|
||||
// o "- Expression", -> new OpNode($1, $2)
|
||||
// o "+ Expression", -> new OpNode($1, $2)
|
||||
// o "NOT Expression", -> new OpNode($1, $2)
|
||||
// o "~ Expression", -> new OpNode($1, $2)
|
||||
// o "-- Expression", -> new OpNode($1, $2)
|
||||
// o "++ Expression", -> new OpNode($1, $2)
|
||||
// o "DELETE Expression", -> new OpNode($1, $2)
|
||||
// o "TYPEOF Expression", -> new OpNode($1, $2)
|
||||
// o "Expression --", -> new OpNode($2, $1, null, true)
|
||||
// o "Expression ++", -> new OpNode($2, $1, null, true)
|
||||
//
|
||||
// o "Expression * Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression / Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression % Expression", -> new OpNode($2, $1, $3)
|
||||
//
|
||||
// o "Expression + Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression - Expression", -> new OpNode($2, $1, $3)
|
||||
//
|
||||
// o "Expression << Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression >> Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression >>> Expression", -> new OpNode($2, $1, $3)
|
||||
//
|
||||
// o "Expression & Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression | Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression ^ Expression", -> new OpNode($2, $1, $3)
|
||||
//
|
||||
// o "Expression <= Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression < Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression > Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression >= Expression", -> new OpNode($2, $1, $3)
|
||||
//
|
||||
// o "Expression == Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression != Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression IS Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression ISNT Expression", -> new OpNode($2, $1, $3)
|
||||
//
|
||||
// o "Expression && Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression || Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression AND Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression OR Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression ? Expression", -> new OpNode($2, $1, $3)
|
||||
//
|
||||
// o "Expression -= Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression += Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression /= Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression *= Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression %= Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression ||= Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression &&= Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression ?= Expression", -> new OpNode($2, $1, $3)
|
||||
//
|
||||
// o "Expression INSTANCEOF Expression", -> new OpNode($2, $1, $3)
|
||||
// o "Expression IN Expression", -> new OpNode($2, $1, $3)
|
||||
// ]
|
||||
// The existence operator.
|
||||
Existence: [o("Expression ?", function() {
|
||||
return new ExistenceNode($1);
|
||||
})
|
||||
],
|
||||
// Function definition.
|
||||
Code: [o("PARAM_START ParamList PARAM_END FuncGlyph Block", function() {
|
||||
return new CodeNode($2, $5, $4);
|
||||
}), o("FuncGlyph Block", function() {
|
||||
return new CodeNode([], $2, $1);
|
||||
})
|
||||
],
|
||||
// The symbols to signify functions, and bound functions.
|
||||
FuncGlyph: [o("->", function() {
|
||||
return 'func';
|
||||
}), o("=>", function() {
|
||||
return 'boundfunc';
|
||||
})
|
||||
],
|
||||
// The parameters to a function definition.
|
||||
ParamList: [o("Param", function() {
|
||||
return [$1];
|
||||
}), o("ParamList , Param", function() {
|
||||
return $1.push($3);
|
||||
})
|
||||
],
|
||||
// A Parameter (or ParamSplat) in a function definition.
|
||||
Param: [o("PARAM", function() {
|
||||
return yytext;
|
||||
}), o("PARAM . . .", function() {
|
||||
return new SplatNode(yytext);
|
||||
})
|
||||
],
|
||||
// A regular splat.
|
||||
Splat: [o("Expression . . .", function() {
|
||||
return new SplatNode($1);
|
||||
})
|
||||
],
|
||||
// Expressions that can be treated as values.
|
||||
Value: [o("IDENTIFIER", function() {
|
||||
return new ValueNode(yytext);
|
||||
}), o("Literal", function() {
|
||||
return new ValueNode($1);
|
||||
}), o("Array", function() {
|
||||
return new ValueNode($1);
|
||||
}), o("Object", function() {
|
||||
return new ValueNode($1);
|
||||
}), o("Parenthetical", function() {
|
||||
return new ValueNode($1);
|
||||
}), o("Range", function() {
|
||||
return new ValueNode($1);
|
||||
}),
|
||||
// o "Value Accessor", -> $1.push($2)
|
||||
o("Invocation Accessor", function() {
|
||||
return new ValueNode($1, [$2]);
|
||||
})
|
||||
]
|
||||
// # Accessing into an object or array, through dot or index notation.
|
||||
// Accessor: [
|
||||
// o "PROPERTY_ACCESS IDENTIFIER", -> new AccessorNode($2)
|
||||
// o "PROTOTYPE_ACCESS IDENTIFIER", -> new AccessorNode($2, 'prototype')
|
||||
// o "SOAK_ACCESS IDENTIFIER", -> new AccessorNode($2, 'soak')
|
||||
// o "Index"
|
||||
// o "Slice", -> new SliceNode($1)
|
||||
// ]
|
||||
//
|
||||
// # Indexing into an object or array.
|
||||
// Index: [
|
||||
// o "INDEX_START Expression INDEX_END", -> new IndexNode($2)
|
||||
// ]
|
||||
//
|
||||
// # An object literal.
|
||||
// Object: [
|
||||
// o "{ AssignList }", -> new ObjectNode($2)
|
||||
// ]
|
||||
//
|
||||
// # Assignment within an object literal (comma or newline separated).
|
||||
// AssignList: [
|
||||
// o "", -> []
|
||||
// o "AssignObj", -> [$1]
|
||||
// o "AssignList , AssignObj", -> $1.push $3
|
||||
// o "AssignList TERMINATOR AssignObj", -> $1.push $3
|
||||
// o "AssignList , TERMINATOR AssignObj", -> $1.push $4
|
||||
// o "INDENT AssignList OUTDENT", -> $2
|
||||
// ]
|
||||
//
|
||||
// # All flavors of function call (instantiation, super, and regular).
|
||||
// Call: [
|
||||
// o "Invocation", -> $1
|
||||
// o "NEW Invocation", -> $2.new_instance()
|
||||
// o "Super", -> $1
|
||||
// ]
|
||||
//
|
||||
// # Extending an object's prototype.
|
||||
// Extends: [
|
||||
// o "Value EXTENDS Value", -> new ExtendsNode($1, $3)
|
||||
// ]
|
||||
//
|
||||
// # A generic function invocation.
|
||||
// Invocation: [
|
||||
// o "Value Arguments", -> new CallNode($1, $2)
|
||||
// o "Invocation Arguments", -> new CallNode($1, $2)
|
||||
// ]
|
||||
//
|
||||
// # The list of arguments to a function invocation.
|
||||
// Arguments: [
|
||||
// o "CALL_START ArgList CALL_END", -> $2
|
||||
// ]
|
||||
//
|
||||
// # Calling super.
|
||||
// Super: [
|
||||
// o "SUPER CALL_START ArgList CALL_END", -> new CallNode('super', $3)
|
||||
// ]
|
||||
//
|
||||
// # The range literal.
|
||||
// Range: [
|
||||
// o "[ Expression . . Expression ]", -> new RangeNode($2, $5)
|
||||
// o "[ Expression . . . Expression ]", -> new RangeNode($2, $6, true)
|
||||
// ]
|
||||
//
|
||||
// # The slice literal.
|
||||
// Slice: [
|
||||
// o "INDEX_START Expression . . Expression INDEX_END", -> new RangeNode($2, $5)
|
||||
// o "INDEX_START Expression . . . Expression INDEX_END", -> new RangeNode($2, $6, true)
|
||||
// ]
|
||||
//
|
||||
// # The array literal.
|
||||
// Array: [
|
||||
// o "[ ArgList ]", -> new ArrayNode($2)
|
||||
// ]
|
||||
//
|
||||
// # A list of arguments to a method call, or as the contents of an array.
|
||||
// ArgList: [
|
||||
// o "", -> []
|
||||
// o "Expression", -> val
|
||||
// o "INDENT Expression", -> [$2]
|
||||
// o "ArgList , Expression", -> $1.push $3
|
||||
// o "ArgList TERMINATOR Expression", -> $1.push $3
|
||||
// o "ArgList , TERMINATOR Expression", -> $1.push $4
|
||||
// o "ArgList , INDENT Expression", -> $1.push $4
|
||||
// o "ArgList OUTDENT", -> $1
|
||||
// ]
|
||||
//
|
||||
// # Just simple, comma-separated, required arguments (no fancy syntax).
|
||||
// SimpleArgs: [
|
||||
// o "Expression", -> $1
|
||||
// o "SimpleArgs , Expression", ->
|
||||
// ([$1].push($3)).reduce (a, b) -> a.concat(b)
|
||||
// ]
|
||||
//
|
||||
// # Try/catch/finally exception handling blocks.
|
||||
// Try: [
|
||||
// o "TRY Block Catch", -> new TryNode($2, $3[0], $3[1])
|
||||
// o "TRY Block FINALLY Block", -> new TryNode($2, nil, nil, $4)
|
||||
// o "TRY Block Catch FINALLY Block", -> new TryNode($2, $3[0], $3[1], $5)
|
||||
// ]
|
||||
//
|
||||
// # A catch clause.
|
||||
// Catch: [
|
||||
// o "CATCH IDENTIFIER Block", -> [$2, $3]
|
||||
// ]
|
||||
//
|
||||
// # Throw an exception.
|
||||
// Throw: [
|
||||
// o "THROW Expression", -> new ThrowNode($2)
|
||||
// ]
|
||||
//
|
||||
// # Parenthetical expressions.
|
||||
// Parenthetical: [
|
||||
// o "( Expression )", -> new ParentheticalNode($2)
|
||||
// ]
|
||||
//
|
||||
// # The while loop. (there is no do..while).
|
||||
// While: [
|
||||
// o "WHILE Expression Block", -> new WhileNode($2, $3)
|
||||
// o "WHILE Expression", -> new WhileNode($2, nil)
|
||||
// o "Expression WHILE Expression", -> new WhileNode($3, Expressions.wrap($1))
|
||||
// ]
|
||||
//
|
||||
// # Array comprehensions, including guard and current index.
|
||||
// # Looks a little confusing, check nodes.rb for the arguments to ForNode.
|
||||
// For: [
|
||||
// o "Expression FOR ForVariables ForSource", -> new ForNode($1, $4, $3[0], $3[1])
|
||||
// o "FOR ForVariables ForSource Block", -> new ForNode($4, $3, $2[0], $2[1])
|
||||
// ]
|
||||
//
|
||||
// # An array comprehension has variables for the current element and index.
|
||||
// ForVariables: [
|
||||
// o "IDENTIFIER", -> [$1]
|
||||
// o "IDENTIFIER , IDENTIFIER", -> [$1, $3]
|
||||
// ]
|
||||
//
|
||||
// # The source of the array comprehension can optionally be filtered.
|
||||
// ForSource: [
|
||||
// o "IN Expression", -> {source: $2}
|
||||
// o "OF Expression", -> {source: $2, object: true}
|
||||
// o "ForSource WHEN Expression", -> $1.filter: $3; $1
|
||||
// o "ForSource BY Expression", -> $1.step: $3; $1
|
||||
// ]
|
||||
//
|
||||
// # Switch/When blocks.
|
||||
// Switch: [
|
||||
// o "SWITCH Expression INDENT Whens OUTDENT", -> $4.rewrite_condition($2)
|
||||
// o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.rewrite_condition($2).add_else($6)
|
||||
// ]
|
||||
//
|
||||
// # The inner list of whens.
|
||||
// Whens: [
|
||||
// o "When", -> $1
|
||||
// o "Whens When", -> $1.push $2
|
||||
// ]
|
||||
//
|
||||
// # An individual when.
|
||||
// When: [
|
||||
// o "LEADING_WHEN SimpleArgs Block", -> new IfNode($2, $3, nil, {statement: true})
|
||||
// o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode($2, $3, nil, {statement: true})
|
||||
// o "Comment TERMINATOR When", -> $3.add_comment($1)
|
||||
// ]
|
||||
//
|
||||
// # The most basic form of "if".
|
||||
// IfBlock: [
|
||||
// o "IF Expression Block", -> new IfNode($2, $3)
|
||||
// ]
|
||||
//
|
||||
// # An elsif portion of an if-else block.
|
||||
// ElsIf: [
|
||||
// o "ELSE IfBlock", -> $2.force_statement()
|
||||
// ]
|
||||
//
|
||||
// # Multiple elsifs can be chained together.
|
||||
// ElsIfs: [
|
||||
// o "ElsIf", -> $1
|
||||
// o "ElsIfs ElsIf", -> $1.add_else($2)
|
||||
// ]
|
||||
//
|
||||
// # Terminating else bodies are strictly optional.
|
||||
// ElseBody: [
|
||||
// o "", -> null
|
||||
// o "ELSE Block", -> $2
|
||||
// ]
|
||||
//
|
||||
// # All the alternatives for ending an if-else block.
|
||||
// IfEnd: [
|
||||
// o "ElseBody", -> $1
|
||||
// o "ElsIfs ElseBody", -> $1.add_else($2)
|
||||
// ]
|
||||
//
|
||||
// # The full complement of if blocks, including postfix one-liner ifs and unlesses.
|
||||
// If: [
|
||||
// o "IfBlock IfEnd", -> $1.add_else($2)
|
||||
// o "Expression IF Expression", -> new IfNode($3, Expressions.wrap($1), nil, {statement: true})
|
||||
// o "Expression UNLESS Expression", -> new IfNode($3, Expressions.wrap($1), nil, {statement: true, invert: true})
|
||||
// ]
|
||||
};
|
||||
// Helpers ==============================================================
|
||||
// Make the Jison parser.
|
||||
bnf = {
|
||||
};
|
||||
tokens = [];
|
||||
__a = grammar;
|
||||
for (name in __a) {
|
||||
non_terminal = __a[name];
|
||||
if (__hasProp.call(__a, name)) {
|
||||
bnf[name] = (function() {
|
||||
__b = []; __c = non_terminal;
|
||||
for (__d = 0; __d < __c.length; __d++) {
|
||||
option = __c[__d];
|
||||
__b.push((function() {
|
||||
__e = option[0].split(" ");
|
||||
for (__f = 0; __f < __e.length; __f++) {
|
||||
part = __e[__f];
|
||||
!grammar[part] ? tokens.push(part) : null;
|
||||
}
|
||||
name === "Root" ? (option[1] = "return " + option[1]) : null;
|
||||
return option;
|
||||
}).call(this));
|
||||
}
|
||||
return __b;
|
||||
}).call(this);
|
||||
}
|
||||
}
|
||||
tokens = tokens.join(" ");
|
||||
parser = new Parser({
|
||||
tokens: tokens,
|
||||
bnf: bnf,
|
||||
operators: operators,
|
||||
startSymbol: 'Root'
|
||||
}, {
|
||||
debug: false
|
||||
});
|
||||
// Thin wrapper around the real lexer
|
||||
parser.lexer = {
|
||||
lex: function lex() {
|
||||
var token;
|
||||
token = this.tokens[this.pos] || [""];
|
||||
this.pos += 1;
|
||||
this.yylineno = token[2];
|
||||
this.yytext = token[1];
|
||||
return token[0];
|
||||
},
|
||||
setInput: function setInput(tokens) {
|
||||
this.tokens = tokens;
|
||||
return this.pos = 0;
|
||||
},
|
||||
upcomingInput: function upcomingInput() {
|
||||
return "";
|
||||
},
|
||||
showPosition: function showPosition() {
|
||||
return this.pos;
|
||||
}
|
||||
};
|
||||
exports.Parser = function Parser() { };
|
||||
exports.Parser.prototype.parse = function parse(tokens) {
|
||||
return parser.parse(tokens);
|
||||
};
|
||||
})();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
||||
(function(){
|
||||
var coffee, prompt, quit, readline, run;
|
||||
// A CoffeeScript port/version of the Node.js REPL.
|
||||
// Required modules.
|
||||
coffee = require('./coffee-script');
|
||||
process.mixin(require('sys'));
|
||||
// Shortcut variables.
|
||||
prompt = 'coffee> ';
|
||||
quit = function quit() {
|
||||
return process.stdio.close();
|
||||
};
|
||||
// The main REPL function. Called everytime a line of code is entered.
|
||||
readline = function readline(code) {
|
||||
return coffee.compile(code, run);
|
||||
};
|
||||
// Attempt to evaluate the command. If there's an exception, print it.
|
||||
run = function run(js) {
|
||||
var val;
|
||||
try {
|
||||
val = eval(js);
|
||||
if (val !== undefined) {
|
||||
p(val);
|
||||
}
|
||||
} catch (err) {
|
||||
puts(err.stack || err.toString());
|
||||
}
|
||||
return print(prompt);
|
||||
};
|
||||
// Start up the REPL.
|
||||
process.stdio.open();
|
||||
process.stdio.addListener('data', readline);
|
||||
print(prompt);
|
||||
})();
|
||||
@@ -1,377 +0,0 @@
|
||||
(function(){
|
||||
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_START, EXPRESSION_TAIL, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, SINGLE_CLOSERS, SINGLE_LINERS, __a, __b, __c, __d, __e, __f, __g, __h, pair, re;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
// In order to keep the grammar simple, the stream of tokens that the Lexer
|
||||
// emits is rewritten by the Rewriter, smoothing out ambiguities, mis-nested
|
||||
// indentation, and single-line flavors of expressions.
|
||||
exports.Rewriter = (re = function re() { });
|
||||
// Tokens that must be balanced.
|
||||
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['PARAM_START', 'PARAM_END'], ['CALL_START', 'CALL_END'], ['INDEX_START', 'INDEX_END']];
|
||||
// Tokens that signal the start of a balanced pair.
|
||||
EXPRESSION_START = (function() {
|
||||
__a = []; __b = BALANCED_PAIRS;
|
||||
for (__c = 0; __c < __b.length; __c++) {
|
||||
pair = __b[__c];
|
||||
__a.push(pair[0]);
|
||||
}
|
||||
return __a;
|
||||
}).call(this);
|
||||
// Tokens that signal the end of a balanced pair.
|
||||
EXPRESSION_TAIL = (function() {
|
||||
__d = []; __e = BALANCED_PAIRS;
|
||||
for (__f = 0; __f < __e.length; __f++) {
|
||||
pair = __e[__f];
|
||||
__d.push(pair[1]);
|
||||
}
|
||||
return __d;
|
||||
}).call(this);
|
||||
// Tokens that indicate the close of a clause of an expression.
|
||||
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_TAIL);
|
||||
// Tokens pairs that, in immediate succession, indicate an implicit call.
|
||||
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END'];
|
||||
IMPLICIT_END = ['IF', 'UNLESS', 'FOR', 'WHILE', 'TERMINATOR', 'OUTDENT'];
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'ARGUMENTS', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT', '->', '=>', '[', '(', '{'];
|
||||
// The inverse mappings of token pairs we're trying to fix up.
|
||||
INVERSES = {
|
||||
};
|
||||
__g = BALANCED_PAIRS;
|
||||
for (__h = 0; __h < __g.length; __h++) {
|
||||
pair = __g[__h];
|
||||
INVERSES[pair[0]] = pair[1];
|
||||
INVERSES[pair[1]] = pair[0];
|
||||
}
|
||||
// Single-line flavors of block expressions that have unclosed endings.
|
||||
// The grammar can't disambiguate them, so we insert the implicit indentation.
|
||||
SINGLE_LINERS = ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN'];
|
||||
SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN', 'PARAM_START'];
|
||||
// Rewrite the token stream in multiple passes, one logical filter at
|
||||
// a time. This could certainly be changed into a single pass through the
|
||||
// stream, with a big ol' efficient switch, but it's much nicer like this.
|
||||
re.prototype.rewrite = function rewrite(tokens) {
|
||||
this.tokens = tokens;
|
||||
this.adjust_comments();
|
||||
this.remove_leading_newlines();
|
||||
this.remove_mid_expression_newlines();
|
||||
this.move_commas_outside_outdents();
|
||||
this.close_open_calls_and_indexes();
|
||||
this.add_implicit_parentheses();
|
||||
this.add_implicit_indentation();
|
||||
this.ensure_balance(BALANCED_PAIRS);
|
||||
this.rewrite_closing_parens();
|
||||
return this.tokens;
|
||||
};
|
||||
// Rewrite the token stream, looking one token ahead and behind.
|
||||
// Allow the return value of the block to tell us how many tokens to move
|
||||
// forwards (or backwards) in the stream, to make sure we don't miss anything
|
||||
// as the stream changes length under our feet.
|
||||
re.prototype.scan_tokens = function scan_tokens(block) {
|
||||
var i, move;
|
||||
i = 0;
|
||||
while (true) {
|
||||
if (!(this.tokens[i])) {
|
||||
break;
|
||||
}
|
||||
move = block(this.tokens[i - 1], this.tokens[i], this.tokens[i + 1], i);
|
||||
i += move;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// Massage newlines and indentations so that comments don't have to be
|
||||
// correctly indented, or appear on their own line.
|
||||
re.prototype.adjust_comments = function adjust_comments() {
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
var after, before;
|
||||
if (!(token[0] === 'COMMENT')) {
|
||||
return 1;
|
||||
}
|
||||
before = this.tokens[i - 2];
|
||||
after = this.tokens[i + 2];
|
||||
if (before && after && ((before[0] === 'INDENT' && after[0] === 'OUTDENT') || (before[0] === 'OUTDENT' && after[0] === 'INDENT')) && before[1] === after[1]) {
|
||||
this.tokens.splice(i + 2, 1);
|
||||
this.tokens.splice(i - 2, 1);
|
||||
return 0;
|
||||
} else if (prev[0] === 'TERMINATOR' && after[0] === 'INDENT') {
|
||||
this.tokens.splice(i + 2, 1);
|
||||
this.tokens[i - 1] = after;
|
||||
return 1;
|
||||
} else if (prev[0] !== 'TERMINATOR' && prev[0] !== 'INDENT' && prev[0] !== 'OUTDENT') {
|
||||
this.tokens.splice(i, 0, ['TERMINATOR', "\n", prev[2]]);
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
// Leading newlines would introduce an ambiguity in the grammar, so we
|
||||
// dispatch them here.
|
||||
re.prototype.remove_leading_newlines = function remove_leading_newlines() {
|
||||
if (this.tokens[0][0] === 'TERMINATOR') {
|
||||
return this.tokens.shift();
|
||||
}
|
||||
};
|
||||
// Some blocks occur in the middle of expressions -- when we're expecting
|
||||
// this, remove their trailing newlines.
|
||||
re.prototype.remove_mid_expression_newlines = function remove_mid_expression_newlines() {
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
if (!(post && EXPRESSION_CLOSE.indexOf(post[0]) >= 0 && token[0] === 'TERMINATOR')) {
|
||||
return 1;
|
||||
}
|
||||
this.tokens.splice(i, 1);
|
||||
return 0;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
// Make sure that we don't accidentally break trailing commas, which need
|
||||
// to go on the outside of expression closers.
|
||||
re.prototype.move_commas_outside_outdents = function move_commas_outside_outdents() {
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
if (token[0] === 'OUTDENT' && prev[0] === ',') {
|
||||
this.tokens.splice(i, 1, token);
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
// We've tagged the opening parenthesis of a method call, and the opening
|
||||
// bracket of an indexing operation. Match them with their close.
|
||||
re.prototype.close_open_calls_and_indexes = function close_open_calls_and_indexes() {
|
||||
var brackets, parens;
|
||||
parens = [0];
|
||||
brackets = [0];
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
if (token[0] === 'CALL_START') {
|
||||
parens.push(0);
|
||||
} else if (token[0] === 'INDEX_START') {
|
||||
brackets.push(0);
|
||||
} else if (token[0] === '(') {
|
||||
parens[parens.length - 1] += 1;
|
||||
} else if (token[0] === '[') {
|
||||
brackets[brackets.length - 1] += 1;
|
||||
} else if (token[0] === ')') {
|
||||
if (parens[parens.length - 1] === 0) {
|
||||
parens.pop();
|
||||
token[0] = 'CALL_END';
|
||||
} else {
|
||||
parens[parens.length - 1] -= 1;
|
||||
}
|
||||
} else if (token[0] === ']') {
|
||||
if (brackets[brackets.length - 1] === 0) {
|
||||
brackets.pop();
|
||||
token[0] = 'INDEX_END';
|
||||
} else {
|
||||
brackets[brackets.length - 1] -= 1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
// Methods may be optionally called without parentheses, for simple cases.
|
||||
// Insert the implicit parentheses here, so that the parser doesn't have to
|
||||
// deal with them.
|
||||
re.prototype.add_implicit_parentheses = function add_implicit_parentheses() {
|
||||
var stack;
|
||||
stack = [0];
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
var __i, __j, __k, __l, idx, last, size, tmp;
|
||||
if (token[0] === 'INDENT') {
|
||||
stack.push(0);
|
||||
}
|
||||
if (token[0] === 'OUTDENT') {
|
||||
last = stack.pop();
|
||||
stack[stack.length - 1] += last;
|
||||
}
|
||||
if (stack[stack.length - 1] > 0 && (IMPLICIT_END.indexOf(token[0]) >= 0 || (typeof !post !== "undefined" && !post !== null))) {
|
||||
idx = token[0] === 'OUTDENT' ? i + 1 : i;
|
||||
__k = 0; __l = stack[stack.length - 1];
|
||||
for (__j=0, tmp=__k; (__k <= __l ? tmp < __l : tmp > __l); (__k <= __l ? tmp += 1 : tmp -= 1), __j++) {
|
||||
this.tokens.splice(idx, 0, ['CALL_END', ')']);
|
||||
}
|
||||
size = stack[stack.length - 1] + 1;
|
||||
stack[stack.length - 1] = 0;
|
||||
return size;
|
||||
}
|
||||
if (!(prev && IMPLICIT_FUNC.indexOf(prev[0]) >= 0 && IMPLICIT_CALL.indexOf(token[0]) >= 0)) {
|
||||
return 1;
|
||||
}
|
||||
this.tokens.splice(i, 0, ['CALL_START', '(']);
|
||||
stack[stack.length - 1] += 1;
|
||||
return 2;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
// Because our grammar is LALR(1), it can't handle some single-line
|
||||
// expressions that lack ending delimiters. Use the lexer to add the implicit
|
||||
// blocks, so it doesn't need to.
|
||||
// ')' can close a single-line block, but we need to make sure it's balanced.
|
||||
re.prototype.add_implicit_indentation = function add_implicit_indentation() {
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
var idx, insertion, parens, starter, tok;
|
||||
if (!(SINGLE_LINERS.indexOf(token[0]) >= 0 && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) {
|
||||
return 1;
|
||||
}
|
||||
starter = token[0];
|
||||
this.tokens.splice(i + 1, 0, ['INDENT', 2]);
|
||||
idx = i + 1;
|
||||
parens = 0;
|
||||
while (true) {
|
||||
idx += 1;
|
||||
tok = this.tokens[idx];
|
||||
if ((!tok || SINGLE_CLOSERS.indexOf(tok[0]) >= 0 || (tok[0] === ')' && parens === 0)) && !(starter === 'ELSE' && tok[0] === 'ELSE')) {
|
||||
insertion = this.tokens[idx - 1][0] === "," ? idx - 1 : idx;
|
||||
this.tokens.splice(insertion, 0, ['OUTDENT', 2]);
|
||||
break;
|
||||
}
|
||||
if (tok[0] === '(') {
|
||||
parens += 1;
|
||||
}
|
||||
if (tok[0] === ')') {
|
||||
parens -= 1;
|
||||
}
|
||||
}
|
||||
if (!(token[0] === 'THEN')) {
|
||||
return 1;
|
||||
}
|
||||
this.tokens.splice(i, 1);
|
||||
return 0;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
// Ensure that all listed pairs of tokens are correctly balanced throughout
|
||||
// the course of the token stream.
|
||||
re.prototype.ensure_balance = function ensure_balance(pairs) {
|
||||
var __i, __j, key, levels, unclosed, value;
|
||||
levels = {
|
||||
};
|
||||
this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
var __i, __j, __k, close, open;
|
||||
__i = pairs;
|
||||
for (__j = 0; __j < __i.length; __j++) {
|
||||
pair = __i[__j];
|
||||
__k = pair;
|
||||
open = __k[0];
|
||||
close = __k[1];
|
||||
levels[open] = levels[open] || 0;
|
||||
if (token[0] === open) {
|
||||
levels[open] += 1;
|
||||
}
|
||||
if (token[0] === close) {
|
||||
levels[open] -= 1;
|
||||
}
|
||||
if (levels[open] < 0) {
|
||||
throw "too many " + token[1];
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
unclosed = (function() {
|
||||
__i = []; __j = levels;
|
||||
for (key in __j) {
|
||||
value = __j[key];
|
||||
if (__hasProp.call(__j, key)) {
|
||||
if (value > 0) {
|
||||
__i.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return __i;
|
||||
}).call(this);
|
||||
if (unclosed.length) {
|
||||
throw "unclosed " + unclosed[0];
|
||||
}
|
||||
};
|
||||
// We'd like to support syntax like this:
|
||||
// el.click((event) ->
|
||||
// el.hide())
|
||||
// In order to accomplish this, move outdents that follow closing parens
|
||||
// inwards, safely. The steps to accomplish this are:
|
||||
//
|
||||
// 1. Check that all paired tokens are balanced and in order.
|
||||
// 2. Rewrite the stream with a stack: if you see an '(' or INDENT, add it
|
||||
// to the stack. If you see an ')' or OUTDENT, pop the stack and replace
|
||||
// it with the inverse of what we've just popped.
|
||||
// 3. Keep track of "debt" for tokens that we fake, to make sure we end
|
||||
// up balanced in the end.
|
||||
re.prototype.rewrite_closing_parens = function rewrite_closing_parens() {
|
||||
var __i, debt, key, stack, val;
|
||||
stack = [];
|
||||
debt = {
|
||||
};
|
||||
__i = INVERSES;
|
||||
for (key in __i) {
|
||||
val = __i[key];
|
||||
if (__hasProp.call(__i, key)) {
|
||||
((debt[key] = 0));
|
||||
}
|
||||
}
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
var inv, match, mtag, tag;
|
||||
tag = token[0];
|
||||
inv = INVERSES[token[0]];
|
||||
// Push openers onto the stack.
|
||||
if (EXPRESSION_START.indexOf(tag) >= 0) {
|
||||
stack.push(token);
|
||||
return 1;
|
||||
// The end of an expression, check stack and debt for a pair.
|
||||
} else if (EXPRESSION_TAIL.indexOf(tag) >= 0) {
|
||||
// If the tag is already in our debt, swallow it.
|
||||
if (debt[inv] > 0) {
|
||||
debt[inv] -= 1;
|
||||
this.tokens.splice(i, 1);
|
||||
return 0;
|
||||
} else {
|
||||
// Pop the stack of open delimiters.
|
||||
match = stack.pop();
|
||||
mtag = match[0];
|
||||
// Continue onwards if it's the expected tag.
|
||||
if (tag === INVERSES[mtag]) {
|
||||
return 1;
|
||||
} else {
|
||||
// Unexpected close, insert correct close, adding to the debt.
|
||||
debt[mtag] += 1;
|
||||
val = mtag === 'INDENT' ? match[1] : INVERSES[mtag];
|
||||
this.tokens.splice(i, 0, [INVERSES[mtag], val]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
};
|
||||
})();
|
||||
@@ -6,8 +6,7 @@ module CoffeeScript
|
||||
class Rewriter
|
||||
|
||||
# Tokens that must be balanced.
|
||||
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], [:INDENT, :OUTDENT],
|
||||
[:PARAM_START, :PARAM_END], [:CALL_START, :CALL_END], [:INDEX_START, :INDEX_END]]
|
||||
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], [:INDENT, :OUTDENT]]
|
||||
|
||||
# Tokens that signal the start of a balanced pair.
|
||||
EXPRESSION_START = BALANCED_PAIRS.map {|pair| pair.first }
|
||||
@@ -18,14 +17,6 @@ module CoffeeScript
|
||||
# Tokens that indicate the close of a clause of an expression.
|
||||
EXPRESSION_CLOSE = [:CATCH, :WHEN, :ELSE, :FINALLY] + EXPRESSION_TAIL
|
||||
|
||||
# Tokens pairs that, in immediate succession, indicate an implicit call.
|
||||
IMPLICIT_FUNC = [:IDENTIFIER, :SUPER, ')', :CALL_END, ']', :INDEX_END]
|
||||
IMPLICIT_END = [:IF, :UNLESS, :FOR, :WHILE, "\n", :OUTDENT]
|
||||
IMPLICIT_CALL = [:IDENTIFIER, :NUMBER, :STRING, :JS, :REGEX, :NEW, :PARAM_START,
|
||||
:TRY, :DELETE, :TYPEOF, :SWITCH,
|
||||
:TRUE, :FALSE, :YES, :NO, :ON, :OFF, '!', '!!', :NOT,
|
||||
'@', '->', '=>', '[', '(', '{']
|
||||
|
||||
# The inverse mappings of token pairs we're trying to fix up.
|
||||
INVERSES = BALANCED_PAIRS.inject({}) do |memo, pair|
|
||||
memo[pair.first] = pair.last
|
||||
@@ -35,8 +26,8 @@ module CoffeeScript
|
||||
|
||||
# Single-line flavors of block expressions that have unclosed endings.
|
||||
# The grammar can't disambiguate them, so we insert the implicit indentation.
|
||||
SINGLE_LINERS = [:ELSE, "->", "=>", :TRY, :FINALLY, :THEN]
|
||||
SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN, :PARAM_START]
|
||||
SINGLE_LINERS = [:ELSE, "=>", :TRY, :FINALLY, :THEN]
|
||||
SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :LEADING_WHEN]
|
||||
|
||||
# Rewrite the token stream in multiple passes, one logical filter at
|
||||
# a time. This could certainly be changed into a single pass through the
|
||||
@@ -44,11 +35,8 @@ module CoffeeScript
|
||||
def rewrite(tokens)
|
||||
@tokens = tokens
|
||||
adjust_comments
|
||||
remove_leading_newlines
|
||||
remove_mid_expression_newlines
|
||||
move_commas_outside_outdents
|
||||
close_open_calls_and_indexes
|
||||
add_implicit_parentheses
|
||||
add_implicit_indentation
|
||||
ensure_balance(*BALANCED_PAIRS)
|
||||
rewrite_closing_parens
|
||||
@@ -81,10 +69,6 @@ module CoffeeScript
|
||||
@tokens.delete_at(i + 2)
|
||||
@tokens.delete_at(i - 2)
|
||||
next 0
|
||||
elsif prev[0] == "\n" && [:INDENT].include?(after[0])
|
||||
@tokens.delete_at(i + 2)
|
||||
@tokens[i - 1] = after
|
||||
next 1
|
||||
elsif !["\n", :INDENT, :OUTDENT].include?(prev[0])
|
||||
@tokens.insert(i, ["\n", Value.new("\n", token[1].line)])
|
||||
next 2
|
||||
@@ -94,12 +78,6 @@ module CoffeeScript
|
||||
end
|
||||
end
|
||||
|
||||
# Leading newlines would introduce an ambiguity in the grammar, so we
|
||||
# dispatch them here.
|
||||
def remove_leading_newlines
|
||||
@tokens.shift if @tokens[0][0] == "\n"
|
||||
end
|
||||
|
||||
# Some blocks occur in the middle of expressions -- when we're expecting
|
||||
# this, remove their trailing newlines.
|
||||
def remove_mid_expression_newlines
|
||||
@@ -122,59 +100,6 @@ module CoffeeScript
|
||||
end
|
||||
end
|
||||
|
||||
# We've tagged the opening parenthesis of a method call, and the opening
|
||||
# bracket of an indexing operation. Match them with their close.
|
||||
def close_open_calls_and_indexes
|
||||
parens, brackets = [0], [0]
|
||||
scan_tokens do |prev, token, post, i|
|
||||
case token[0]
|
||||
when :CALL_START then parens.push(0)
|
||||
when :INDEX_START then brackets.push(0)
|
||||
when '(' then parens[-1] += 1
|
||||
when '[' then brackets[-1] += 1
|
||||
when ')'
|
||||
if parens.last == 0
|
||||
parens.pop
|
||||
token[0] = :CALL_END
|
||||
else
|
||||
parens[-1] -= 1
|
||||
end
|
||||
when ']'
|
||||
if brackets.last == 0
|
||||
brackets.pop
|
||||
token[0] = :INDEX_END
|
||||
else
|
||||
brackets[-1] -= 1
|
||||
end
|
||||
end
|
||||
next 1
|
||||
end
|
||||
end
|
||||
|
||||
# Methods may be optionally called without parentheses, for simple cases.
|
||||
# Insert the implicit parentheses here, so that the parser doesn't have to
|
||||
# deal with them.
|
||||
def add_implicit_parentheses
|
||||
stack = [0]
|
||||
scan_tokens do |prev, token, post, i|
|
||||
stack.push(0) if token[0] == :INDENT
|
||||
if token[0] == :OUTDENT
|
||||
last = stack.pop
|
||||
stack[-1] += last
|
||||
end
|
||||
if stack.last > 0 && (IMPLICIT_END.include?(token[0]) || post.nil?)
|
||||
idx = token[0] == :OUTDENT ? i + 1 : i
|
||||
stack.last.times { @tokens.insert(idx, [:CALL_END, Value.new(')', token[1].line)]) }
|
||||
size, stack[-1] = stack[-1] + 1, 0
|
||||
next size
|
||||
end
|
||||
next 1 unless IMPLICIT_FUNC.include?(prev[0]) && IMPLICIT_CALL.include?(token[0])
|
||||
@tokens.insert(i, [:CALL_START, Value.new('(', token[1].line)])
|
||||
stack[-1] += 1
|
||||
next 2
|
||||
end
|
||||
end
|
||||
|
||||
# Because our grammar is LALR(1), it can't handle some single-line
|
||||
# expressions that lack ending delimiters. Use the lexer to add the implicit
|
||||
# blocks, so it doesn't need to.
|
||||
@@ -183,7 +108,6 @@ module CoffeeScript
|
||||
scan_tokens do |prev, token, post, i|
|
||||
next 1 unless SINGLE_LINERS.include?(token[0]) && post[0] != :INDENT &&
|
||||
!(token[0] == :ELSE && post[0] == :IF) # Elsifs shouldn't get blocks.
|
||||
starter = token[0]
|
||||
line = token[1].line
|
||||
@tokens.insert(i + 1, [:INDENT, Value.new(2, line)])
|
||||
idx = i + 1
|
||||
@@ -191,11 +115,9 @@ module CoffeeScript
|
||||
loop do
|
||||
idx += 1
|
||||
tok = @tokens[idx]
|
||||
if (!tok || SINGLE_CLOSERS.include?(tok[0]) ||
|
||||
(tok[0] == ')' && parens == 0)) &&
|
||||
!(starter == :ELSE && tok[0] == :ELSE)
|
||||
insertion = @tokens[idx - 1][0] == "," ? idx - 1 : idx
|
||||
@tokens.insert(insertion, [:OUTDENT, Value.new(2, line)])
|
||||
if !tok || SINGLE_CLOSERS.include?(tok[0]) ||
|
||||
(tok[0] == ')' && parens == 0)
|
||||
@tokens.insert(idx, [:OUTDENT, Value.new(2, line)])
|
||||
break
|
||||
end
|
||||
parens += 1 if tok[0] == '('
|
||||
@@ -210,25 +132,22 @@ module CoffeeScript
|
||||
# Ensure that all listed pairs of tokens are correctly balanced throughout
|
||||
# the course of the token stream.
|
||||
def ensure_balance(*pairs)
|
||||
puts "\nbefore ensure_balance: #{@tokens.inspect}" if ENV['VERBOSE']
|
||||
levels, lines = Hash.new(0), Hash.new
|
||||
levels = Hash.new(0)
|
||||
scan_tokens do |prev, token, post, i|
|
||||
pairs.each do |pair|
|
||||
open, close = *pair
|
||||
levels[open] += 1 if token[0] == open
|
||||
levels[open] -= 1 if token[0] == close
|
||||
lines[token[0]] = token[1].line
|
||||
raise ParseError.new(token[0], token[1], nil) if levels[open] < 0
|
||||
end
|
||||
next 1
|
||||
end
|
||||
unclosed = levels.detect {|k, v| v > 0 }
|
||||
sym = unclosed && unclosed[0]
|
||||
raise ParseError.new(sym, Value.new(sym, lines[sym]), nil, "unclosed '#{sym}'") if unclosed
|
||||
raise SyntaxError, "unclosed '#{unclosed[0]}'" if unclosed
|
||||
end
|
||||
|
||||
# We'd like to support syntax like this:
|
||||
# el.click((event) ->
|
||||
# el.click(event =>
|
||||
# el.hide())
|
||||
# In order to accomplish this, move outdents that follow closing parens
|
||||
# inwards, safely. The steps to accomplish this are:
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
(function(){
|
||||
var coffee, paths;
|
||||
// Quickie script to compile and run all the files given as arguments.
|
||||
process.mixin(require('sys'));
|
||||
coffee = require('./coffee-script');
|
||||
paths = process.ARGV;
|
||||
paths = paths.slice(2, paths.length);
|
||||
paths.length ? coffee.compile_files(paths, function(js) {
|
||||
return eval(js);
|
||||
}) : require('./repl');
|
||||
})();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user