Compare commits

...

136 Commits
0.1.6 ... 0.2.0

Author SHA1 Message Date
Jeremy Ashkenas
0e645cce41 alright, done fiddling. CoffeeScript 0.2.0 2010-01-05 00:45:04 -05:00
Jeremy Ashkenas
4f564dec60 more docs 2010-01-05 00:34:18 -05:00
Jeremy Ashkenas
20144abe25 tweaking docs 2010-01-05 00:19:22 -05:00
Jeremy Ashkenas
a8631dcbaf more docs for 0.2 -- blocks and splats 2010-01-04 23:26:27 -05:00
Jeremy Ashkenas
f5cd6578b8 adding the complete underscore.coffee example to the docs 2010-01-04 22:51:02 -05:00
Jeremy Ashkenas
f1af50ee33 first pass at 0.2.0 docs 2010-01-04 22:19:45 -05:00
Jeremy Ashkenas
ad5e69d303 minor doc updates -- let's try pulling in the underscore test suite 2010-01-04 19:15:24 -05:00
Jeremy Ashkenas
7d800b5e5b removed the whole messy notions of looking downwards for returns or children -- ForNodes now peek at top-level status, and if they're being asked to return a value from the outside 2010-01-04 18:57:10 -05:00
Jeremy Ashkenas
f150011d9d nicer scope inspects 2010-01-04 09:43:50 -05:00
Jeremy Ashkenas
a09f807ce0 more underscore 2010-01-04 01:43:45 -05:00
Jeremy Ashkenas
6082b9dc36 putting in a special check for returns within array comprehensions -- not very nice 2010-01-04 01:06:31 -05:00
Jeremy Ashkenas
326904826d pretty amazing -- fully functional draft of underscore.js 0.5.2, all in CoffeeScript 2010-01-04 00:28:52 -05:00
Jeremy Ashkenas
3f30712ca1 fixing a nasty little bug with not dup'ing a string in Scope.rb, causing later functions to start their free_variables where previous functions left off, because they shared their ancestor's @temp_variable string 2010-01-04 00:16:38 -05:00
Jeremy Ashkenas
e9b72ee955 more underscore examples raised a slight bug with a lexing ambiguity between leading whens (in switches), and trailing whens (in comprehensions) -- made two different tokens to distinguish them 2010-01-03 22:25:38 -05:00
Jeremy Ashkenas
7be3b8edac mo' expression examples 2010-01-03 19:08:41 -05:00
Jeremy Ashkenas
3daac200e5 -- 2010-01-03 19:00:08 -05:00
Jeremy Ashkenas
f77877d7eb adding a statement-as-expression test, and returning null from while loops, if asked 2010-01-03 18:58:34 -05:00
Jeremy Ashkenas
536bdd2107 updating fixtures -- all tests now pass -- back to master 2010-01-03 18:49:00 -05:00
Jeremy Ashkenas
0093455d29 logic error in Expressions was causing over-compilation by a factor of the depth of the tree 2010-01-03 18:47:23 -05:00
Jeremy Ashkenas
67d34ec40b fixing comment-within-objecta-and-array-literal printing for the new expression regime. 2010-01-03 18:35:03 -05:00
Jeremy Ashkenas
d8603dbff2 change lexical scoping example to use var names that haven't been already defined 2010-01-03 18:27:26 -05:00
Jeremy Ashkenas
6b6cb3ab12 subtle call order bug was preventing variable declarations 2010-01-03 18:22:10 -05:00
Jeremy Ashkenas
10f53bac15 lowering the precedence of if/else/while 2010-01-03 18:11:53 -05:00
Jeremy Ashkenas
15f12423c3 removing the silly newlines from comments 2010-01-03 18:07:03 -05:00
Jeremy Ashkenas
885dbaf7e4 beautiful -- all examples, tests, and docs are now compiling without JSLint warnings 2010-01-03 16:32:59 -05:00
Jeremy Ashkenas
0d0df09c6d All execution tests are now passing with statements everywhere 2010-01-03 15:59:33 -05:00
Jeremy Ashkenas
7df7ff26c8 more progress -- you can wrap parentheses around statements now 2010-01-03 15:13:59 -05:00
Jeremy Ashkenas
841258b360 first rough rough rough draft of kamatsu's closure suggestion -- test.coffee runs, but probably nothing else 2010-01-03 13:59:17 -05:00
Jeremy Ashkenas
f715393b06 bumping version numbers to 0.2.0 in anticipation of release soon-ish 2010-01-03 11:01:50 -05:00
Jeremy Ashkenas
abfe3c35f8 adding a block test and using PARAM_SPLAT to remove the last shift/reduce conflict 2010-01-03 10:46:37 -05:00
Jeremy Ashkenas
8517455746 Adding kamatsu's proposed block literal syntax 2010-01-03 10:19:39 -05:00
Jeremy Ashkenas
0e86fa534e todo 2010-01-02 01:00:03 -05:00
Jeremy Ashkenas
1dbf257df7 adding splice literals, with tests 2010-01-02 00:20:24 -05:00
Jeremy Ashkenas
5cbd94bf14 rebuilding narwhal uncovered a bug with named functions 2010-01-01 22:00:34 -05:00
Jeremy Ashkenas
fa3f3e41d4 fixing the food/eat array comprehension in the docs to not pretend like there's a made-up method on String.prototype 2010-01-01 17:16:34 -05:00
Jeremy Ashkenas
94bab256b4 adding consistent highlighting to variable assignment, whether functions or values 2010-01-01 17:11:48 -05:00
Jeremy Ashkenas
dfa2f50076 better existence test, with tests 2010-01-01 12:41:55 -05:00
Jeremy Ashkenas
e2de88544d adding the notion of existence -- postfixing an expression with a question mark will check if to see if it's not null or undefined 2010-01-01 12:31:05 -05:00
Jeremy Ashkenas
010a2dde11 commenting the lexer a bit more 2010-01-01 12:11:35 -05:00
Jeremy Ashkenas
2fb8f48e75 expanding the list of tokens that regexes may not follow, according to the Mozilla JS 2.0 docs 2010-01-01 12:08:36 -05:00
Jeremy Ashkenas
271ea24b20 adding steps to range comprehensions 2010-01-01 11:54:59 -05:00
Jeremy Ashkenas
792fd359bd fixing precedence order, so that you can nest range comprehensions 2010-01-01 11:19:57 -05:00
Jeremy Ashkenas
acf4a5ee47 making range comprehensions compile safely, even when you assign to the same variable as your endposts. 2010-01-01 10:55:43 -05:00
Jeremy Ashkenas
d9afe2cf44 -- 2010-01-01 10:40:29 -05:00
Jeremy Ashkenas
ee304485b8 allowing indentation within function calls 2010-01-01 10:38:28 -05:00
Jeremy Ashkenas
5957ba624a adding a test for expressions in range comprehensions 2010-01-01 10:20:29 -05:00
Jeremy Ashkenas
2b40607b57 test for uminus 2010-01-01 10:17:33 -05:00
Jeremy Ashkenas
8da4185bbf allowing expressions within range literals 2010-01-01 10:15:22 -05:00
Jeremy Ashkenas
e8df8abf7a fixing the regex lexer to make it less agressive when we know it can't possibly be a regex 2010-01-01 09:49:18 -05:00
Jeremy Ashkenas
b7ef9510c9 fix for multiple splats in a function call 2009-12-31 20:02:15 -05:00
Jeremy Ashkenas
fba131c5a4 adding splats as arguments to function calls 2009-12-31 19:52:13 -05:00
Jeremy Ashkenas
4985a400e6 adding a note in the docs about how to build the parser and install the gem 2009-12-31 18:22:51 -05:00
Jeremy Ashkenas
2ed041b5e1 comment about test_execution being the most important 2009-12-31 18:09:48 -05:00
Jeremy Ashkenas
5bcb2b2629 reserving variables for splats the regular way, not through a custom 'var' declaration 2009-12-31 18:03:39 -05:00
Jeremy Ashkenas
adca8183de adding splats to function definitions 2009-12-31 17:50:12 -05:00
Jeremy Ashkenas
c187f2160f expressions nested in expressions made for some indentation issues -- statements are now responsible for their own leading indentation 2009-12-31 16:50:46 -05:00
Jeremy Ashkenas
959c9a31cb adding a test for multiline-array-comprehension-with-filter 2009-12-31 16:13:52 -05:00
Jeremy Ashkenas
9f6233473e enabling multi-line array and object comprehensions 2009-12-31 16:09:27 -05:00
Jeremy Ashkenas
32b0f9fa4f adding a filtered object comprehension test 2009-12-31 15:08:54 -05:00
Jeremy Ashkenas
f7e49eaae4 using push for comprehension results so that it works with object keys, and adding a test for object comprehensions 2009-12-31 15:03:32 -05:00
Jeremy Ashkenas
3042a50f87 adding weepy's suggestion to use (for .. in) for array comprehensions, which means that they're now object comprehensions as well 2009-12-31 14:52:14 -05:00
Jeremy Ashkenas
c8e820e851 done commenting the rewriter 2009-12-31 13:45:07 -05:00
Jeremy Ashkenas
66f92e770d detailed scan_tokens so that the calling function can indicate the number of spaces to move forward (or backward) in the token stream 2009-12-31 13:43:24 -05:00
Jeremy Ashkenas
f3472b7437 making assignment token detection a regex like all the others 2009-12-31 13:26:38 -05:00
Jeremy Ashkenas
fdf2a76c53 pulled out all token-stream-rewriting logic into the CoffeeScript::Rewriter -- let the lexer be simpleminded 2009-12-31 13:22:33 -05:00
Jeremy Ashkenas
d11569b434 adding a rake:ultraviolet build syntax highlighter task, and regenerating the docs with correct highlighting 2009-12-31 13:01:10 -05:00
Jeremy Ashkenas
86be82f3ae adding a test case for named functions 2009-12-31 10:52:00 -05:00
Jeremy Ashkenas
bcc5aa2bc3 updating docs -- need to get back on the computer that has the syntax highlighter for UV installed 2009-12-30 23:43:55 -05:00
Jeremy Ashkenas
5658b2b41f updating tests for named functions 2009-12-30 23:14:29 -05:00
Jeremy Ashkenas
34bf4ce325 making all functions named functions, if children of an immediate assignment 2009-12-30 23:13:22 -05:00
Jeremy Ashkenas
4251aa30c6 adding proper auto-newline escaping 2009-12-30 22:49:25 -05:00
Jeremy Ashkenas
3ad9316fd0 allowing any manner of indentation in the comments, by adjusting them in the lexer 2009-12-30 22:24:40 -05:00
Jeremy Ashkenas
e4ae324e8f don't print the confusing indentation numbers when raising ParseErrors for indentation 2009-12-30 21:57:03 -05:00
Jeremy Ashkenas
370d05148d getting there, finally ... all tests are green for whitespace 2009-12-30 21:51:23 -05:00
Jeremy Ashkenas
3f27b0ff72 the underscore example parses now -- added line number information to parenthetical nodes 2009-12-30 21:44:51 -05:00
Jeremy Ashkenas
71cf6ab031 moving the newline escaping detection up higher so indents don't overrule it 2009-12-30 21:41:01 -05:00
Jeremy Ashkenas
f3e9a18c3c lex indents with higher precedence than comments 2009-12-30 21:20:30 -05:00
Jeremy Ashkenas
d2176acf25 more fiddling with the lexer -- the indentation is super fragile 2009-12-30 21:15:54 -05:00
Jeremy Ashkenas
62725c5a52 more fiddling with the lexer -- the indentation is super fragile 2009-12-30 21:11:54 -05:00
Jeremy Ashkenas
cc66b31e69 rolling back MULTI_DENT regex 2009-12-30 20:41:32 -05:00
Jeremy Ashkenas
199d314903 regex cleanup -- eliminating some lookahead because Ruby regexps blow chunks (stackoverflows) when you look (ahead) at them funny. 2009-12-30 20:36:47 -05:00
Jeremy Ashkenas
f2a805db24 fixed up the comment/assignment interleaving in nodes.rb 2009-12-30 20:24:24 -05:00
Jeremy Ashkenas
b7e29aac4d rewrote 'rewrite_closing_parens' with an explicit loop -- there was a bug when adding to @tokens in the middle of scan_tokens' while loop -- consider scan_tokens to be on probation until further notice 2009-12-30 20:12:30 -05:00
Jeremy Ashkenas
2ee04b8421 be more vigorous about removing mid-expression newlines, 'when' closes implicit blocks, a better comment-detecting regex lexer that doesn't eat outdents 2009-12-30 19:26:37 -05:00
Jeremy Ashkenas
65ce74fbcb big milestone. examples/code.coffee now compiles correctly under the new whitespace regime 2009-12-30 18:59:33 -05:00
Jeremy Ashkenas
4b520d04e3 balancing parens closing single-line blocks 2009-12-30 18:52:03 -05:00
Jeremy Ashkenas
4eb10f9d43 fixing up documents example 2009-12-30 18:28:16 -05:00
Jeremy Ashkenas
7e58d1d914 adding ')' as a SINGLE_CLOSER, although it's probably unsafe 2009-12-30 18:09:43 -05:00
Jeremy Ashkenas
4097a81456 parser and test tweaks for whitespace -- tests are coming along 2009-12-30 17:58:27 -05:00
Jeremy Ashkenas
c4413b933b removed the final shift/reduce errors -- back to zero for the first time in a long time 2009-12-30 17:45:47 -05:00
Jeremy Ashkenas
e3da53e3df special case for 'else if' in the lexer 2009-12-30 17:41:14 -05:00
Jeremy Ashkenas
675a5f5d7c execution tests still pass -- more lexer block insertion and 2 shift/reduces in the grammar now 2009-12-30 15:52:07 -05:00
Jeremy Ashkenas
02c19b3170 patching up the lexer and adding a test with trailing whitespace (it was too string for trailing whitespace before) 2009-12-30 15:10:47 -05:00
Jeremy Ashkenas
96859e749b fixing up narwhal integration (again) 2009-12-30 15:05:05 -05:00
Jeremy Ashkenas
622ddea343 fixin up narwhal factory and adding more implicit blocks to the lexer 2009-12-30 14:32:59 -05:00
Jeremy Ashkenas
8e8efe4288 patched up lexer to add indentation to single-line flavors of statements -- let's expand this idea 2009-12-30 13:58:00 -05:00
Jeremy Ashkenas
df5c5d9fe2 merged in master branch again 2009-12-30 13:38:50 -05:00
Jeremy Ashkenas
cf7ce8a1af fixing the double-printing bug with coffee -r 2009-12-30 13:34:25 -05:00
Jeremy Ashkenas
b3cd5721cf ignoring test.coffee 2009-12-30 12:59:05 -05:00
Jeremy Ashkenas
893908b8fe removing dots from whitespace examples 2009-12-30 00:22:27 -05:00
Jeremy Ashkenas
6821660905 clean up a couple of test errors for whitespace 2009-12-30 00:08:49 -05:00
Jeremy Ashkenas
bc0214730a touch-ups cleanups to the lexer and rebuilding the narwhal libs from whitespace'd versions 2009-12-29 23:01:08 -05:00
Jeremy Ashkenas
57d0f25054 implementing kamatsu's debt-based lexer for closing delimiters 2009-12-29 22:24:52 -05:00
Jeremy Ashkenas
f456d1b78e whitespace parser down to 4 shift/reduce errors -- good enough for me 2009-12-29 21:46:15 -05:00
Jeremy Ashkenas
332f499c31 put the commas on the outside of expression closers 2009-12-29 20:39:51 -05:00
Jeremy Ashkenas
92cdeb093e don't break trailing commas with rewrite_closing_parens 2009-12-29 10:20:18 -05:00
Jeremy Ashkenas
f2bdd555fa killing some newlines in the execution tests, to test the lexer's newline suppression 2009-12-29 10:02:19 -05:00
Jeremy Ashkenas
5c7dee556a changing array comprehension filters from 'where' to 'when' to mirror case/when 2009-12-29 09:55:37 -05:00
Jeremy Ashkenas
8c6e5d0b37 allowing indentation in object and array literals 2009-12-29 09:25:56 -05:00
Jeremy Ashkenas
1128beb49b still some kinks to work out -- mid-expression blocks 2009-12-29 09:18:41 -05:00
Jeremy Ashkenas
0963eea60e using 'where' for array comprehension filtering, after kamatsu's suggestion -- execution tests pass now with significant whitespace 2009-12-29 08:52:26 -05:00
Jeremy Ashkenas
c1cdedd260 moving along with whitespace 2009-12-28 23:08:02 -05:00
Jeremy Ashkenas
3b0b93ec06 first draft of kamatsu's rewrite rules -- finally got whitespace to be flexible enough, I think. 2009-12-28 21:07:47 -05:00
Jeremy Ashkenas
9c2f66ff13 got lexer balancing parens, indent/outdents, brackets, and curlies 2009-12-28 21:02:40 -05:00
Jeremy Ashkenas
1b688d7077 merging in master 2009-12-28 20:06:23 -05:00
Jeremy Ashkenas
df1f9c27eb removed unused example 2009-12-28 20:05:14 -05:00
Jeremy Ashkenas
e227a3bc69 fixing relative path for execution tests 2009-12-28 16:23:48 -05:00
Jeremy Ashkenas
e4c6119550 stop shifting args 2009-12-28 16:20:11 -05:00
Jeremy Ashkenas
6c9e8f28b6 rebuilding narwhal libs 2009-12-28 16:10:56 -05:00
Jeremy Ashkenas
cc7e685428 merging in tlrobinson's fix for package.json 2009-12-28 16:09:14 -05:00
tlrobinson
4abd88f2a9 Add package.json to gemspec files so Narwhal integrations works when installed as a gem. 2009-12-28 12:45:47 -08:00
Jeremy Ashkenas
a44fe402a1 removing broken accidental commit 2009-12-28 09:02:55 -08:00
tlrobinson
350cb623ae Add package on command line in case it's not installed in a Narwhal packages path. 2009-12-28 01:49:07 -08:00
tlrobinson
42c9c53a4c Merge branch 'master' of git://github.com/jashkenas/coffee-script 2009-12-28 01:36:23 -08:00
tlrobinson
5a49c22121 Fixed Narwhal integration. Cleaned up module organization, etc. 2009-12-28 01:16:57 -08:00
Jeremy Ashkenas
2bc4cbbdcc part of the way to supporting multiline array comprehensions -- the grammar and parsing is there -- the code generation is tricky 2009-12-27 21:50:02 -08:00
Jeremy Ashkenas
6aa247f73d that's it for now for significant whitespace -- I really just can't make flexible enough 2009-12-26 19:29:59 -08:00
Jeremy Ashkenas
da71735066 smarter but uglier lexer -- now handles most significant whitespace cases. Clean it up though... (newlines after outdents) 2009-12-26 13:22:53 -08:00
Jeremy Ashkenas
d6ac6a3535 removing no_paren -- can cause order of operations errors 2009-12-26 11:10:59 -08:00
Jeremy Ashkenas
c322d77b86 got a lexer working along the lines of what kamatsu proposes 2009-12-26 10:59:47 -08:00
Jeremy Ashkenas
7aa69579ff little more progress on whitespace 2009-12-26 10:49:11 -08:00
Jeremy Ashkenas
5f3e2b7fc7 merging in master 2009-12-26 09:59:06 -08:00
Jeremy Ashkenas
7f066aa168 ... 2009-12-24 15:05:14 -08:00
Jeremy Ashkenas
166ea578f9 outdent lexing is correct now, I think 2009-12-24 14:51:53 -08:00
Jeremy Ashkenas
cb3b47690a this might not work for ))) outdent cases 2009-12-24 14:34:48 -08:00
Jeremy Ashkenas
cf6060bdb3 first, totally broken branch of significant whitespace -- it can handle examples/whitespace.cs though 2009-12-24 13:48:46 -08:00
89 changed files with 4705 additions and 2727 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
test.coffee
parser.output
test/fixtures/underscore
*.gem

View File

@@ -19,7 +19,14 @@ namespace :build do
desc "Compile the Narwhal interface for --interactive and --run"
task :narwhal do
sh "bin/coffee lib/coffee_script/narwhal/*.coffee -o lib/coffee_script/narwhal/js"
sh "bin/coffee lib/coffee_script/narwhal/*.coffee -o lib/coffee_script/narwhal/lib/coffee-script"
sh "mv lib/coffee_script/narwhal/lib/coffee-script/coffee-script.js lib/coffee_script/narwhal/lib/coffee-script.js"
end
desc "Compile and install the Ultraviolet syntax highlighter"
task :ultraviolet do
sh "plist2syntax lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage"
sh "sudo mv coffeescript.yaml /usr/local/lib/ruby/gems/1.8/gems/ultraviolet-0.10.2/syntax/coffeescript.syntax"
end
end
@@ -30,6 +37,7 @@ task :doc do
child = fork { exec "bin/coffee documentation/coffee/*.coffee -o documentation/js -w" }
at_exit { Process.kill("INT", child) }
Signal.trap("INT") { exit }
# `uv -t idle -s coffeescript -h examples/underscore.coffee > documentation/underscore.html`
loop do
mtime = File.stat(source).mtime
if !@mtime || mtime > @mtime

View File

@@ -1,7 +1,7 @@
Gem::Specification.new do |s|
s.name = 'coffee-script'
s.version = '0.1.6' # Keep version in sync with coffee-script.rb
s.date = '2009-12-27'
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,5 +22,5 @@ Gem::Specification.new do |s|
s.require_paths = ['lib']
s.executables = ['coffee']
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README']
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README', 'package.json']
end

View File

@@ -4,4 +4,4 @@ volume: 10 if band isnt spinal_tap
let_the_wild_rumpus_begin() unless answer is no
if car.speed < speed_limit then accelerate().
if car.speed < speed_limit then accelerate()

View File

@@ -1,5 +1,7 @@
# Eat lunch.
lunch: food.eat() for food in ['toast', 'cheese', 'wine'].
lunch: eat(food) for food in ['toast', 'cheese', 'wine']
# Zebra-stripe a table.
highlight(row) for row, i in table if i % 2 is 0.
# Naive collision detection.
for roid in asteroids
for roid2 in asteroids when roid isnt roid2
roid.explode() if roid.overlaps(roid2)

View File

@@ -0,0 +1,4 @@
$('table.list').each() table =>
$('tr.account', table).each() row =>
row.show()
row.highlight()

View File

@@ -2,8 +2,8 @@ mood: greatly_improved if singing
if happy and knows_it
claps_hands()
cha_cha_cha().
cha_cha_cha()
date: if friday then sue else jill.
date: if friday then sue else jill
expensive ||= do_the_math()

View File

@@ -0,0 +1 @@
solipsism: true if mind? and not world?

View File

@@ -2,8 +2,8 @@ grade: student =>
if student.excellent_work
"A+"
else if student.okay_stuff
if student.tried_hard then "B" else "B-".
if student.tried_hard then "B" else "B-"
else
"C"..
"C"
eldest: if 24 > 21 then "Liz" else "Ike".
eldest: if 24 > 21 then "Liz" else "Ike"

View File

@@ -0,0 +1,3 @@
# The first ten global properties.
globals: (name for property, name in window)[0...10]

View File

@@ -0,0 +1,6 @@
alert(
try
nonexistent / undefined
catch error
"The error is: " + error
)

View File

@@ -1,2 +1,2 @@
square: x => x * x.
cube: x => square(x) * x.
square: x => x * x
cube: x => square(x) * x

View File

@@ -0,0 +1,3 @@
years_old: {max: 10, ida: 9, tim: 11}
ages: child + " is " + age for age, child in years_old

View File

@@ -1,6 +1,13 @@
song: ["do", "re", "mi", "fa", "so"]
ages: {
max: 10
ida: 9
tim: 11
}
}
matrix: [
1, 0, 1
0, 0, 1
1, 1, 0
]

View File

@@ -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,8 +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)
# Existence:
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

View File

@@ -0,0 +1,3 @@
for i in [0...eggs.length] by 12
dozen_eggs: eggs[i...i+12]
deliver(new egg_carton(dozen))

View File

@@ -1,5 +1,5 @@
num: 1
change_numbers: =>
num: 2
new_num: 3.
new_num: -1
num: 10
new_num: change_numbers()

View File

@@ -0,0 +1,25 @@
gold: silver: the_field: "unknown"
medalists: first, second, *rest =>
gold: first
silver: second
the_field: rest
contenders: [
"Michael Phelps"
"Liu Xiang"
"Yao Ming"
"Allyson Felix"
"Shawn Johnson"
"Roman Sebrle"
"Guo Jingjing"
"Tyson Gay"
"Asafa Powell"
"Usain Bolt"
]
medalists(*contenders)
alert("Gold: " + gold)
alert("Silver: " + silver)
alert("The Field: " + the_field)

View File

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

View File

@@ -1,18 +1,18 @@
Animal: => .
Animal: =>
Animal.prototype.move: meters =>
alert(this.name + " moved " + meters + "m.").
alert(this.name + " moved " + meters + "m.")
Snake: name => this.name: name.
Snake: name => this.name: name
Snake extends Animal
Snake.prototype.move: =>
alert("Slithering...")
super(5).
super(5)
Horse: name => this.name: name.
Horse: name => this.name: name
Horse extends Animal
Horse.prototype.move: =>
alert("Galloping...")
super(45).
super(45)
sam: new Snake("Sammy the Python")
tom: new Horse("Tommy the Palomino")

View File

@@ -1,9 +1,9 @@
switch day
when "Tuesday" then eat_breakfast()
when "Wednesday" then go_to_the_park()
when "Saturday"
if day is bingo_day
go_to_bingo()
go_dancing().
when "Sunday" then go_to_church()
else go_to_work().
when "Tuesday" then eat_breakfast()
when "Wednesday" then go_to_the_park()
when "Saturday"
if day is bingo_day
go_to_bingo()
go_dancing()
when "Sunday" then go_to_church()
else go_to_work()

View File

@@ -4,4 +4,4 @@ try
catch error
print(error)
finally
clean_up().
clean_up()

View File

@@ -1,5 +1,5 @@
while demand > supply
sell()
restock().
restock()
while supply > demand then buy().
while supply > demand then buy()

View File

@@ -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)
@@ -49,23 +64,31 @@
preserved intact.
</p>
<p>
<b>Latest Version:</b>
<a href="http://gemcutter.org/gems/coffee-script">0.2.0</a>
</p>
<h2>Table of Contents</h2>
<p>
<a href="#overview">Mini Overview</a><br />
<a href="#installation">Installation and Usage</a><br />
<a href="#punctuation">Punctuation Primer</a><br />
<a href="#whitespace">Significant Whitespace</a><br />
<a href="#functions">Functions and Invocation</a><br />
<a href="#assignment">Assignment</a><br />
<a href="#objects_and_arrays">Objects and Arrays</a><br />
<a href="#lexical_scope">Lexical Scoping and Variable Safety</a><br />
<a href="#conditionals">Conditionals, Ternaries, and Conditional Assignment</a><br />
<a href="#expressions">Everything is an Expression</a><br />
<a href="#existence">The Existence Operator</a><br />
<a href="#aliases">Aliases</a><br />
<a href="#splats">Splats</a><br />
<a href="#while">While Loops</a><br />
<a href="#array_comprehensions">Array Comprehensions</a><br />
<a href="#slice">Slicing Arrays with Ranges</a><br />
<a href="#comprehensions">Comprehensions (Arrays, Objects, and Ranges)</a><br />
<a href="#slice_splice">Array Slicing and Splicing with Ranges</a><br />
<a href="#expressions">Everything is an Expression</a><br />
<a href="#inheritance">Inheritance, and Calling Super from a Subclass</a><br />
<a href="#blocks">Blocks</a><br />
<a href="#embedded">Embedded JavaScript</a><br />
<a href="#switch">Switch/When/Else</a><br />
<a href="#try">Try/Catch/Finally</a><br />
@@ -74,13 +97,22 @@
<a href="#contributing">Contributing</a><br />
<a href="#change_log">Change Log</a><br />
</p>
<h2 id="overview">Mini Overview</h2>
<p><i>CoffeeScript on the left, compiled JavaScript output on the right.</i></p>
<%= code_for('overview', 'cubed_list') %>
<p>
For a longer CoffeeScript example, check out
<a href="documentation/underscore.html">Underscore.coffee</a>, a port
of <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>
<h2 id="installation">Installation and Usage</h2>
<p>
@@ -113,7 +145,7 @@ gem install coffee-script</pre>
<tr>
<td><code>-r, --run</code></td>
<td>
Compile and execute the CoffeeScripts without saving the intermediate
Compile and execute scripts without saving the intermediate
JavaScript. Requires <a href="http://narwhaljs.org/">Narwhal</a>.
</td>
</tr>
@@ -149,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>
@@ -205,22 +237,23 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</i>
</p>
<p id="punctuation">
<b class="header">Punctuation Primer</b>
You don't need to use semicolons <tt>;</tt> to terminate expressions, ending
the line will do just as well. All other whitespace is
not significant. Instead of using curly braces <tt>{ }</tt>
to delimit a block of code, use a period <tt>.</tt> to mark the end of a
block, for
<a href="#functions">functions</a>,
<p id="whitespace">
<b class="header">Significant Whitespace</b>
CoffeeScript uses Python-style significant whitespace: You don't need to
use semicolons <tt>;</tt> to terminate expressions, ending
the line will do just as well. Semicolons can still be used to fit
multiple expressions onto a single line. Instead of using curly braces
<tt>{ }</tt> to delimit blocks of code (like <a href="#functions">functions</a>,
<a href="#conditionals">if-statements</a>,
<a href="#switch">switch</a>, and <a href="#try">try/catch</a>.
<a href="#switch">switch</a>, and <a href="#try">try/catch</a>),
use indentation.
</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>
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)') %>
@@ -232,8 +265,8 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('assignment', 'greeting') %>
<p>
Declarations of new variables are pushed up to the top of the current scope,
so that assignments may always be used within expressions.
Declarations of new variables are pushed up to the top of the nearest
lexical scope, so that assignment may always be performed within expressions.
</p>
<p id="objects_and_arrays">
@@ -241,7 +274,8 @@ 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.
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(",")') %>
@@ -253,25 +287,29 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('scope', 'new_num') %>
<p>
Notice how the variables are declared with <tt>var</tt> the first time
they appear. The second reference of <b>num</b>, within the function,
is not redeclared because <b>num</b> is still in scope. As opposed
to the second occurrence of <b>new_num</b>, in the last line.
Notice how the all of the variable declarations have been pushed up to
the top of the closest scope, the first time they appear.
<b>num</b> is not redeclared within the inner function, because it's
already in scope; the <b>new_num</b> within the function, on the other hand,
should not be able to change the value of the external variable of the same name, and
therefore has a declaration of its own.
</p>
<p>
Although suppressed within this documentation for clarity, all
CoffeeScript output is wrapped in an anonymous function:
<tt>(function(){ ... })();</tt> This safety wrapper, combined with the
automatic generation of the <tt>var</tt> keyword, make it exceedingly difficult
to pollute the global namespace by accident.
to pollute the global namespace by accident. If you'd like to create
global variables, attach them as properties on <b>window</b>,
or on the <b>exports</b> object in CommonJS.
</p>
<p id="conditionals">
<b class="header">Conditionals, Ternaries, and Conditional Assignment</b>
<b>If/else</b> statements can be written without the use of parenthesis and
curly brackets. As with functions and other block expressions, conditionals
are closed with periods. No period is necessary when using the single-line
postfix form, with the <tt>if</tt> at the end.
<b>If/else</b> statements can be written without the use of parentheses and
curly brackets. As with functions and other block expressions,
multi-line conditionals are delimited by indentation. There's also a handy
postfix form, with the <tt>if</tt> or <tt>unless</tt> at the end.
</p>
<p>
CoffeeScript will compile <b>if</b> statements using the ternary operator
@@ -279,31 +317,21 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('conditionals') %>
<p>
The conditional assignment operators are available: <tt>||=</tt>,
The conditional assignment operators are included: <tt>||=</tt>,
which only assigns a value to a variable if the variable's current value
is falsy, and <tt>&amp;&amp;=</tt>, which only replaces the value of
truthy variables.
</p>
<p id="expressions">
<b class="header">Everything is an Expression (at least, as much as possible)</b>
You might have noticed how even though we don't add return statements
to CoffeeScript functions, they nonetheless return their final value.
The CoffeeScript compiler tries to make sure that all statements in the
language can be used as expressions. Watch how the <tt>return</tt> gets
pushed down into each possible branch of execution, in the function
below.
<p 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('expressions', 'eldest') %>
<p>
The same mechanism is used to push down assignment through <b>switch</b>
statements, and <b>if-elses</b> (although the ternary operator is preferred).
Another part of manipulating assignment statements is
CoffeeScript's declaration of new variables at the top of the
current scope. This allows assignment to be used as a piece of an
expression.
</p>
<%= code_for('expressions_assignment', 'six') %>
<%= code_for('existence') %>
<p id="aliases">
<b class="header">Aliases</b>
@@ -335,6 +363,15 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('aliases') %>
<p id="splats">
<b class="header">Splats</b>
The JavaScript <b>arguments object</b> is a useful way to work with
functions that accept variable numbers of arguments. CoffeeScript provides
splats <tt>*</tt>, both for function definition as well as invocation,
making variable arguments a little bit more palatable.
</p>
<%= code_for('splats', true) %>
<p id="while">
<b class="header">While Loops</b>
The only low-level loop that CoffeeScript provides is the while loop.
@@ -347,8 +384,8 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
<b>each</b> (<b>forEach</b>) style iterators, or...
</p>
<p id="array_comprehensions">
<b class="header">Array Comprehensions</b>
<p id="comprehensions">
<b class="header">Comprehensions (Arrays, Objects, and Ranges)</b>
For your looping needs, CoffeeScript provides array comprehensions
similar to Python's. They replace (and compile into) <b>for</b> loops, with
optional guard clauses and the value of the current array index.
@@ -358,13 +395,19 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('array_comprehensions') %>
<p>
If you're not iterating over an actual array, you can use a range to
specify the start and end of an array comprehension:
<tt>coundown(i) for i in [10..1].</tt>
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:
</p>
<%= code_for('range_comprehensions') %>
<p>
Comprehensions can also be used to iterate over the values and keys in
an object:
</p>
<%= code_for('object_comprehensions', 'ages.join(", ")') %>
<p id="slice">
<b class="header">Slicing Arrays with Ranges</b>
<p id="slice_splice">
<b class="header">Array Slicing and Splicing with Ranges</b>
CoffeeScript borrows Ruby's
<a href="http://ruby-doc.org/core/classes/Range.html">range syntax</a>
for extracting slices of arrays. With two dots (<tt>3..5</tt>), the range
@@ -373,6 +416,39 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
a range that excludes the end.
</p>
<%= code_for('slices', 'numbers_copy') %>
<p>
The same syntax can be used with assignment to replace a segment of an
array with new values (to splice it).
</p>
<%= code_for('splices', 'numbers') %>
<p id="expressions">
<b class="header">Everything is an Expression (at least, as much as possible)</b>
You might have noticed how even though we don't add return statements
to CoffeeScript functions, they nonetheless return their final value.
The CoffeeScript compiler tries to make sure that all statements in the
language can be used as expressions. Watch how the <tt>return</tt> gets
pushed down into each possible branch of execution, in the function
below.
</p>
<%= code_for('expressions', 'eldest') %>
<p>
Because variable declarations occur at the top of scope, assignment can
be used within expressions, even for variables that haven't been seen before:
</p>
<%= code_for('expressions_assignment', 'six') %>
<p>
Things that would otherwise be statements in JavaScript, when used
as part of an expression in CoffeeScript, are converted into expressions
by wrapping them in a closure. This lets you do useful things, like assign
the result of a comprehension to a variable:
</p>
<%= code_for('expressions_comprehension', 'globals') %>
<p>
As well as silly things, like passing a <b>try/catch</b> statement directly
into a function call:
</p>
<%= code_for('expressions_try', true) %>
<p id="inheritance">
<b class="header">Inheritance, and Calling Super from a Subclass</b>
@@ -393,6 +469,15 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('super', true) %>
<p id="blocks">
<b class="header">Blocks</b>
Many common looping functions (in Prototype, jQuery, and Underscore,
for example) take a single function as their final argument. To make
final functions easier to pass, CoffeeScript includes block syntax,
so you don't have to close the parentheses on the other side.
</p>
<%= code_for('blocks') %>
<p id="embedded">
<b class="header">Embedded JavaScript</b>
If you ever need to interpolate literal JavaScript snippets, you can
@@ -419,18 +504,32 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</p>
<%= code_for('try') %>
<p id="try">
<p id="strings">
<b class="header">Multiline Strings</b>
Multiline strings are allowed in CoffeeScript.
</p>
<%= code_for('strings', 'moby_dick') %>
<h2 id="resources">Resources</h2>
<p>
<a href="http://github.com/jashkenas/coffee-script/">Source Code</a><br />
<a href="http://github.com/jashkenas/coffee-script/issues">Bugs and Feature Requests</a><br />
</p>
<ul>
<li>
<a href="http://github.com/jashkenas/coffee-script/">Source Code</a><br />
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">Bugs and Feature Requests</a>
</li>
<li>
<a href="http://github.com/jnicklas/bistro_car">BistroCar</a><br />
A Rails plugin by
<a href="http://github.com/jnicklas">Jonas Nicklas</a>
that includes CoffeeScript helpers,
bundling and minification.
</li>
</ul>
<h2 id="contributing">Contributing</h2>
@@ -441,13 +540,13 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
<ul>
<li>
A JavaScript version of the compiler, perhaps using Alessandro Warth's
<a href="http://tinlizzie.org/ometa/">OMeta</a>.
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>
Ideas for alternate syntax to end blocks of expressions &mdash; the periods
can look a little weird with deeply nested structure. (There's now a
'whitespace' branch &mdash; help add significant whitespace over there.)
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
@@ -469,19 +568,29 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</ul>
<h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 20px;">0.2.0</b>
Major release. Significant whitespace. Better statement-to-expression
conversion. Splats. Splice literals. Object comprehensions. Blocks.
The existence operator. Many thanks to all the folks who posted issues,
with special thanks to
<a href="http://github.com/kamatsu">Liam O'Connor-Davis</a> for whitespace
and expression help.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.6</b>
Bugfix for running <tt>coffee --interactive</tt> and <tt>--run</tt>
from outside of the CoffeeScript directory. Bugfix for nested
function/if-statements.
from outside of the CoffeeScript directory. Bugfix for nested
function/if-statements.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.5</b>
Array slice literals and array comprehensions can now both take Ruby-style
ranges to specify the start and end. JavaScript variable declaration is
now pushed up to the top of the scope, making all assignment statements into
ranges to specify the start and end. JavaScript variable declaration is
now pushed up to the top of the scope, making all assignment statements into
expressions. You can use <tt>\</tt> to escape newlines.
The <tt>coffee-script</tt> command is now called <tt>coffee</tt>.
</p>

View File

@@ -1,20 +1,34 @@
(function(){
var __a, __b, __c, __d, __e, __f, __g, __h, food, i, lunch, row;
var __a, __b, __c, __d, __e, __f, __g, __h, __i, __j, food, lunch, roid, roid2;
// Eat lunch.
__a = ['toast', 'cheese', 'wine'];
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
food = __a[__b];
__d[__b] = food.eat();
lunch = (function() {
__a = ['toast', 'cheese', 'wine'];
__c = [];
for (__b in __a) {
if (__a.hasOwnProperty(__b)) {
food = __a[__b];
__d = eat(food);
__c.push(__d);
}
}
return __c;
})();
// Naive collision detection.
__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();
}
}
}
}
}
}
lunch = __d;
// Zebra-stripe a table.
__e = table;
__h = [];
for (__f=0, __g=__e.length; __f<__g; __f++) {
row = __e[__f];
i = __f;
__h[__f] = i % 2 === 0 ? highlight(row) : null;
}
__h;
})();

View File

@@ -0,0 +1,8 @@
(function(){
$('table.list').each(function(table) {
return $('tr.account', table).each(function(row) {
row.show();
return row.highlight();
});
});
})();

View File

@@ -0,0 +1,6 @@
(function(){
var solipsism;
if ((typeof mind !== "undefined" && mind !== null) && !(typeof world !== "undefined" && world !== null)) {
solipsism = true;
}
})();

View File

@@ -1,6 +1,6 @@
(function(){
var eldest, grade;
grade = function(student) {
grade = function grade(student) {
if (student.excellent_work) {
return "A+";
} else if (student.okay_stuff) {

View File

@@ -0,0 +1,16 @@
(function(){
var __a, __b, __c, globals, name, property;
// The first ten global properties.
globals = ((function() {
__a = window;
__b = [];
for (name in __a) {
if (__a.hasOwnProperty(name)) {
property = __a[name];
__c = name;
__b.push(__c);
}
}
return __b;
})()).slice(0, 10);
})();

View File

@@ -0,0 +1,9 @@
(function(){
alert((function() {
try {
return nonexistent / undefined;
} catch (error) {
return "The error is: " + error;
}
})());
})();

View File

@@ -1,9 +1,9 @@
(function(){
var cube, square;
square = function(x) {
square = function square(x) {
return x * x;
};
cube = function(x) {
cube = function cube(x) {
return square(x) * x;
};
})();

View File

@@ -0,0 +1,20 @@
(function(){
var __a, __b, __c, age, ages, child, years_old;
years_old = {
max: 10,
ida: 9,
tim: 11
};
ages = (function() {
__a = years_old;
__b = [];
for (child in __a) {
if (__a.hasOwnProperty(child)) {
age = __a[child];
__c = child + " is " + age;
__b.push(__c);
}
}
return __b;
})();
})();

View File

@@ -1,9 +1,10 @@
(function(){
var ages, song;
var ages, matrix, song;
song = ["do", "re", "mi", "fa", "so"];
ages = {
max: 10,
ida: 9,
tim: 11
};
matrix = [1, 0, 1, 0, 0, 1, 1, 1, 0];
})();

View File

@@ -1,5 +1,5 @@
(function(){
var __a, __b, __c, __d, cubed_list, list, math, num, number, opposite_day, square;
var __a, __b, __c, __d, cubed_list, list, math, num, number, opposite_day, race, square;
// Assignment:
number = 42;
opposite_day = true;
@@ -8,7 +8,7 @@
number = -42;
}
// Functions:
square = function(x) {
square = function square(x) {
return x * x;
};
// Arrays:
@@ -17,16 +17,31 @@
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
cube: function cube(x) {
return x * square(x);
}
};
// Array comprehensions:
__a = list;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
num = __a[__b];
__d[__b] = math.cube(num);
// Splats:
race = function race(winner) {
var runners;
runners = Array.prototype.slice.call(arguments, 1);
return print(winner, runners);
};
// Existence:
if ((typeof elvis !== "undefined" && elvis !== null)) {
alert("I knew it!");
}
cubed_list = __d;
// Array comprehensions:
cubed_list = (function() {
__a = list;
__c = [];
for (__b in __a) {
if (__a.hasOwnProperty(__b)) {
num = __a[__b];
__d = math.cube(num);
__c.push(__d);
}
}
return __c;
})();
})();

View File

@@ -0,0 +1,9 @@
(function(){
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));
}
})();

View File

@@ -1,10 +1,10 @@
(function(){
var change_numbers, new_num, num;
num = 1;
change_numbers = function() {
change_numbers = function change_numbers() {
var new_num;
num = 2;
return (new_num = 3);
new_num = -1;
return num = 10;
};
new_num = change_numbers();
})();

View File

@@ -0,0 +1,16 @@
(function(){
var contenders, gold, medalists, silver, the_field;
gold = silver = the_field = "unknown";
medalists = function medalists(first, second) {
var rest;
rest = Array.prototype.slice.call(arguments, 2);
gold = first;
silver = second;
return the_field = rest;
};
contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];
medalists.apply(this, contenders);
alert("Gold: " + gold);
alert("Silver: " + silver);
alert("The Field: " + the_field);
})();

View File

@@ -0,0 +1,5 @@
(function(){
var numbers;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
numbers.splice.apply(numbers, [3, 6 - 3 + 1].concat([-3, -4, -5, -6]));
})();

View File

@@ -1,27 +1,27 @@
(function(){
var Animal, Horse, Snake, sam, tom;
Animal = function() {
Animal = function Animal() {
};
Animal.prototype.move = function(meters) {
Animal.prototype.move = function move(meters) {
return alert(this.name + " moved " + meters + "m.");
};
Snake = function(name) {
return (this.name = name);
Snake = function Snake(name) {
return this.name = name;
};
Snake.__superClass__ = Animal.prototype;
Snake.prototype = new Animal();
Snake.prototype.constructor = Snake;
Snake.prototype.move = function() {
Snake.prototype.move = function move() {
alert("Slithering...");
return Snake.__superClass__.move.call(this, 5);
};
Horse = function(name) {
return (this.name = name);
Horse = function Horse(name) {
return this.name = name;
};
Horse.__superClass__ = Animal.prototype;
Horse.prototype = new Animal();
Horse.prototype.constructor = Horse;
Horse.prototype.move = function() {
Horse.prototype.move = function move() {
alert("Galloping...");
return Horse.__superClass__.move.call(this, 45);
};

View File

@@ -0,0 +1,616 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<link rel="stylesheet" type="text/css" media="screen,projection,print" href="css/idle.css" />
<title>Underscore.coffee</title>
<style type="text/css">
body {
margin: 0; padding: 0;
}
pre.idle {
font-family: "Monaco", "Consolas", monospace;
font-size: 12px;
}
</style>
</head>
<body>
<pre class="idle"><span class="line-numbers"> 1 </span>
<span class="line-numbers"> 2 </span> <span class="Comment"><span class="Comment">#</span> Underscore.coffee</span>
<span class="line-numbers"> 3 </span> <span class="Comment"><span class="Comment">#</span> (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.</span>
<span class="line-numbers"> 4 </span> <span class="Comment"><span class="Comment">#</span> Underscore is freely distributable under the terms of the MIT license.</span>
<span class="line-numbers"> 5 </span> <span class="Comment"><span class="Comment">#</span> Portions of Underscore are inspired by or borrowed from Prototype.js,</span>
<span class="line-numbers"> 6 </span> <span class="Comment"><span class="Comment">#</span> Oliver Steele's Functional, and John Resig's Micro-Templating.</span>
<span class="line-numbers"> 7 </span> <span class="Comment"><span class="Comment">#</span> For all details and documentation:</span>
<span class="line-numbers"> 8 </span> <span class="Comment"><span class="Comment">#</span> http://documentcloud.github.com/underscore/</span>
<span class="line-numbers"> 9 </span>
<span class="line-numbers"> 10 </span>
<span class="line-numbers"> 11 </span> <span class="Comment"><span class="Comment">#</span> ------------------------- Baseline setup ---------------------------------</span>
<span class="line-numbers"> 12 </span>
<span class="line-numbers"> 13 </span> <span class="Comment"><span class="Comment">#</span> Establish the root object, &quot;window&quot; in the browser, or &quot;global&quot; on the server.</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 &quot;_&quot; variable.</span>
<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">obj</span> <span class="Storage">=&gt;</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> 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 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">=&gt;</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> 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> 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> _.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">obj, iterator, context</span> <span class="Storage">=&gt;</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> 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
<span class="line-numbers"> 67 </span>
<span class="line-numbers"> 68 </span>
<span class="line-numbers"> 69 </span> <span class="Comment"><span class="Comment">#</span> Return the results of applying the iterator to each element. Use JavaScript</span>
<span class="line-numbers"> 70 </span> <span class="Comment"><span class="Comment">#</span> 1.6's version of map, if possible.</span>
<span class="line-numbers"> 71 </span> <span class="FunctionName">_.map</span><span class="Keyword">:</span> <span class="FunctionArgument">obj, iterator, context</span> <span class="Storage">=&gt;</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> results<span class="Keyword">:</span> []
<span class="line-numbers"> 74 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</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">obj, memo, iterator, context</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 82 </span> <span class="Keyword">return</span> obj.reduce(_.bind(iterator, context), memo) <span class="Keyword">if</span> (obj <span class="Keyword">and</span> _.isFunction(obj.reduce))
<span class="line-numbers"> 83 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</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">obj, memo, iterator, context</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 91 </span> <span class="Keyword">return</span> obj.reduceRight(_.bind(iterator, context), memo) <span class="Keyword">if</span> (obj <span class="Keyword">and</span> _.isFunction(obj.reduceRight))
<span class="line-numbers"> 92 </span> _.each(_.clone(_.toArray(obj)).reverse())<span class="FunctionArgument"> value, index </span><span class="Storage">=&gt;</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">obj, iterator, context</span> <span class="Storage">=&gt;</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">=&gt;</span>
<span class="line-numbers"> 101 </span> <span class="Keyword">if</span> iterator.call(context, value, index, list)
<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">obj, iterator, context</span> <span class="Storage">=&gt;</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> results<span class="Keyword">:</span> []
<span class="line-numbers"> 112 </span> _.each(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</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">obj, iterator, context</span> <span class="Storage">=&gt;</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">=&gt;</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">obj, iterator, context</span> <span class="Storage">=&gt;</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> 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">=&gt;</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">obj, iterator, context</span> <span class="Storage">=&gt;</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> 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">=&gt;</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">obj, target</span> <span class="Storage">=&gt;</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> 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">obj, method</span> <span class="Storage">=&gt;</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">obj, key</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 164 </span> _.map(obj, (<span class="FunctionArgument">val </span><span class="Storage">=&gt;</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">obj, iterator, context</span> <span class="Storage">=&gt;</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> 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">=&gt;</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">&gt;=</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">obj, iterator, context</span> <span class="Storage">=&gt;</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> 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">=&gt;</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">&lt;</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">obj, iterator, context</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 189 </span> _.pluck(((_.map(obj)<span class="FunctionArgument"> value, index, list </span><span class="Storage">=&gt;</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">=&gt;</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">&lt;</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">&gt;</span> b <span class="Keyword">then</span> <span class="Number">1</span> <span class="Keyword">else</span> <span class="Number">0</span>
<span class="line-numbers"> 194 </span> ), <span class="String"><span class="String">'</span>value<span class="String">'</span></span>)
<span class="line-numbers"> 195 </span>
<span class="line-numbers"> 196 </span>
<span class="line-numbers"> 197 </span> <span class="Comment"><span class="Comment">#</span> Use a comparator function to figure out at what index an object should</span>
<span class="line-numbers"> 198 </span> <span class="Comment"><span class="Comment">#</span> be inserted so as to maintain order. Uses binary search.</span>
<span class="line-numbers"> 199 </span> <span class="FunctionName">_.sortedIndex</span><span class="Keyword">:</span> <span class="FunctionArgument">array, obj, iterator</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 200 </span> iterator <span class="Keyword">||</span><span class="Keyword">=</span> _.identity
<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">&lt;</span> high
<span class="line-numbers"> 203 </span> mid<span class="Keyword">:</span> (low <span class="Keyword">+</span> high) <span class="Keyword">&gt;</span><span class="Keyword">&gt;</span> <span class="Number">1</span>
<span class="line-numbers"> 204 </span> <span class="Keyword">if</span> iterator(array[mid]) <span class="Keyword">&lt;</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">iterable</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 210 </span> <span class="Keyword">return</span> [] <span class="Keyword">if</span> (<span class="Keyword">!</span>iterable)
<span class="line-numbers"> 211 </span> <span class="Keyword">return</span> iterable.toArray() <span class="Keyword">if</span> (iterable.toArray)
<span class="line-numbers"> 212 </span> <span class="Keyword">return</span> iterable <span class="Keyword">if</span> (_.isArray(iterable))
<span class="line-numbers"> 213 </span> <span class="Keyword">return</span> slice.call(iterable) <span class="Keyword">if</span> (_.isArguments(iterable))
<span class="line-numbers"> 214 </span> _.values(iterable)
<span class="line-numbers"> 215 </span>
<span class="line-numbers"> 216 </span>
<span class="line-numbers"> 217 </span> <span class="Comment"><span class="Comment">#</span> Return the number of elements in an object.</span>
<span class="line-numbers"> 218 </span> <span class="FunctionName">_.size</span><span class="Keyword">:</span> <span class="FunctionArgument">obj</span> <span class="Storage">=&gt;</span> _.toArray(obj).length
<span class="line-numbers"> 219 </span>
<span class="line-numbers"> 220 </span>
<span class="line-numbers"> 221 </span> <span class="Comment"><span class="Comment">#</span> -------------------------- Array Functions: ------------------------------</span>
<span class="line-numbers"> 222 </span>
<span class="line-numbers"> 223 </span> <span class="Comment"><span class="Comment">#</span> Get the first element of an array. Passing &quot;n&quot; 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 &quot;head&quot;. The &quot;guard&quot; 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">array, n, guard</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 227 </span> <span class="Keyword">if</span> n <span class="Keyword">and</span> <span class="Keyword">not</span> guard <span class="Keyword">then</span> slice.call(array, <span class="Number">0</span>, n) <span class="Keyword">else</span> array[<span class="Number">0</span>]
<span class="line-numbers"> 228 </span>
<span class="line-numbers"> 229 </span>
<span class="line-numbers"> 230 </span> <span class="Comment"><span class="Comment">#</span> Returns everything but the first entry of the array. Aliased as &quot;tail&quot;.</span>
<span class="line-numbers"> 231 </span> <span class="Comment"><span class="Comment">#</span> Especially useful on the arguments object. Passing an &quot;index&quot; 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 &quot;guard&quot;</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">array, index, guard</span> <span class="Storage">=&gt;</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">array</span> <span class="Storage">=&gt;</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">array</span> <span class="Storage">=&gt;</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">array</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 248 </span> _.reduce(array, [])<span class="FunctionArgument"> memo, value </span><span class="Storage">=&gt;</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">array</span> <span class="Storage">=&gt;</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">array, isSorted</span> <span class="Storage">=&gt;</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
<span class="line-numbers"> 267 </span>
<span class="line-numbers"> 268 </span>
<span class="line-numbers"> 269 </span> <span class="Comment"><span class="Comment">#</span> Produce an array that contains every item shared between all the</span>
<span class="line-numbers"> 270 </span> <span class="Comment"><span class="Comment">#</span> passed-in arrays.</span>
<span class="line-numbers"> 271 </span> <span class="FunctionName">_.intersect</span><span class="Keyword">:</span> <span class="FunctionArgument">array</span> <span class="Storage">=&gt;</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">=&gt;</span>
<span class="line-numbers"> 274 </span> _.all(rest)<span class="FunctionArgument"> other </span><span class="Storage">=&gt;</span>
<span class="line-numbers"> 275 </span> _.indexOf(other, item) <span class="Keyword">&gt;=</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">=&gt;</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="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">=&gt;</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="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">=&gt;</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="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">=&gt;</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">&lt;=</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">&lt;=</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">&gt;</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">&gt;=</span> <span class="Number">0</span>
<span class="line-numbers"> 325 </span> range[idx]<span class="Keyword">:</span> i
<span class="line-numbers"> 326 </span> idx<span class="Keyword">++</span>
<span class="line-numbers"> 327 </span> i<span class="Keyword">+</span><span class="Keyword">=</span> step
<span class="line-numbers"> 328 </span>
<span class="line-numbers"> 329 </span>
<span class="line-numbers"> 330 </span> <span class="Comment"><span class="Comment">#</span> ----------------------- Function Functions: -----------------------------</span>
<span class="line-numbers"> 331 </span>
<span class="line-numbers"> 332 </span> <span class="Comment"><span class="Comment">#</span> Create a function bound to a given object (assigning 'this', and arguments,</span>
<span class="line-numbers"> 333 </span> <span class="Comment"><span class="Comment">#</span> optionally). Binding with arguments is also known as 'curry'.</span>
<span class="line-numbers"> 334 </span> <span class="FunctionName">_.bind</span><span class="Keyword">:</span> <span class="FunctionArgument">func, obj</span> <span class="Storage">=&gt;</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">=&gt;</span> func.apply(obj <span class="Keyword">or</span> root, args.concat(_.toArray(arguments)))
<span class="line-numbers"> 337 </span>
<span class="line-numbers"> 338 </span>
<span class="line-numbers"> 339 </span> <span class="Comment"><span class="Comment">#</span> Bind all of an object's methods to that object. Useful for ensuring that</span>
<span class="line-numbers"> 340 </span> <span class="Comment"><span class="Comment">#</span> all callbacks defined on an object belong to it.</span>
<span class="line-numbers"> 341 </span> <span class="FunctionName">_.bindAll</span><span class="Keyword">:</span> <span class="FunctionArgument">obj</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 342 </span> funcs<span class="Keyword">:</span> <span class="Keyword">if</span> arguments.length <span class="Keyword">&gt;</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">=&gt;</span> obj[f]<span class="Keyword">:</span> _.bind(obj[f], obj)))
<span class="line-numbers"> 344 </span> obj
<span class="line-numbers"> 345 </span>
<span class="line-numbers"> 346 </span>
<span class="line-numbers"> 347 </span> <span class="Comment"><span class="Comment">#</span> Delays a function for the given number of milliseconds, and then calls</span>
<span class="line-numbers"> 348 </span> <span class="Comment"><span class="Comment">#</span> it with the arguments supplied.</span>
<span class="line-numbers"> 349 </span> <span class="FunctionName">_.delay</span><span class="Keyword">:</span> <span class="FunctionArgument">func, wait</span> <span class="Storage">=&gt;</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">=&gt;</span> func.apply(func, args)), wait)
<span class="line-numbers"> 352 </span>
<span class="line-numbers"> 353 </span>
<span class="line-numbers"> 354 </span> <span class="Comment"><span class="Comment">#</span> Defers a function, scheduling it to run after the current call stack has</span>
<span class="line-numbers"> 355 </span> <span class="Comment"><span class="Comment">#</span> cleared.</span>
<span class="line-numbers"> 356 </span> <span class="FunctionName">_.defer</span><span class="Keyword">:</span> <span class="FunctionArgument">func</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 357 </span> _.delay.apply(_, [func, <span class="Number">1</span>].concat(_.rest(arguments)))
<span class="line-numbers"> 358 </span>
<span class="line-numbers"> 359 </span>
<span class="line-numbers"> 360 </span> <span class="Comment"><span class="Comment">#</span> Returns the first function passed as an argument to the second,</span>
<span class="line-numbers"> 361 </span> <span class="Comment"><span class="Comment">#</span> allowing you to adjust arguments, run code before and after, and</span>
<span class="line-numbers"> 362 </span> <span class="Comment"><span class="Comment">#</span> conditionally execute the original function.</span>
<span class="line-numbers"> 363 </span> <span class="FunctionName">_.wrap</span><span class="Keyword">:</span> <span class="FunctionArgument">func, wrapper</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 364 </span> <span class="FunctionArgument"> </span><span class="Storage">=&gt;</span> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))
<span class="line-numbers"> 365 </span>
<span class="line-numbers"> 366 </span>
<span class="line-numbers"> 367 </span> <span class="Comment"><span class="Comment">#</span> Returns a function that is the composition of a list of functions, each</span>
<span class="line-numbers"> 368 </span> <span class="Comment"><span class="Comment">#</span> consuming the return value of the function that follows.</span>
<span class="line-numbers"> 369 </span> <span class="FunctionName">_.compose</span><span class="Keyword">:</span> <span class="Storage">=&gt;</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">=&gt;</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="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">=&gt;</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="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">=&gt;</span>
<span class="line-numbers"> 388 </span> _.map(obj, _.identity)
<span class="line-numbers"> 389 </span>
<span class="line-numbers"> 390 </span>
<span class="line-numbers"> 391 </span> <span class="Comment"><span class="Comment">#</span> Return a sorted list of the function names available in Underscore.</span>
<span class="line-numbers"> 392 </span> <span class="FunctionName">_.functions</span><span class="Keyword">:</span> <span class="FunctionArgument">obj</span> <span class="Storage">=&gt;</span>
<span class="line-numbers"> 393 </span> _.select(_.keys(obj)<span class="FunctionArgument">, key </span><span class="Storage">=&gt;</span> _.isFunction(obj[key])).sort()
<span class="line-numbers"> 394 </span>
<span class="line-numbers"> 395 </span>
<span class="line-numbers"> 396 </span> <span class="Comment"><span class="Comment">#</span> Extend a given object with all of the properties in a source object.</span>
<span class="line-numbers"> 397 </span> <span class="FunctionName">_.extend</span><span class="Keyword">:</span> <span class="FunctionArgument">destination, source</span> <span class="Storage">=&gt;</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="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">=&gt;</span>
<span class="line-numbers"> 405 </span> <span class="Keyword">return</span> obj.slice(<span class="Number">0</span>) <span class="Keyword">if</span> _.isArray(obj)
<span class="line-numbers"> 406 </span> _.extend({}, obj)
<span class="line-numbers"> 407 </span>
<span class="line-numbers"> 408 </span>
<span class="line-numbers"> 409 </span> <span class="Comment"><span class="Comment">#</span> Invokes interceptor with the obj, and then returns obj.</span>
<span class="line-numbers"> 410 </span> <span class="Comment"><span class="Comment">#</span> The primary purpose of this method is to &quot;tap into&quot; 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">=&gt;</span>
<span class="line-numbers"> 412 </span> interceptor(obj)
<span class="line-numbers"> 413 </span> obj
<span class="line-numbers"> 414 </span>
<span class="line-numbers"> 415 </span>
<span class="line-numbers"> 416 </span> <span class="Comment"><span class="Comment">#</span> Perform a deep comparison to check if two objects are equal.</span>
<span class="line-numbers"> 417 </span> <span class="FunctionName">_.isEqual</span><span class="Keyword">:</span> <span class="FunctionArgument">a, b</span> <span class="Storage">=&gt;</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="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">=&gt;</span> _.keys(obj).length <span class="Keyword">is</span> <span class="Number">0</span>
<span class="line-numbers"> 454 </span>
<span class="line-numbers"> 455 </span>
<span class="line-numbers"> 456 </span> <span class="Comment"><span class="Comment">#</span> Is a given value a DOM element?</span>
<span class="line-numbers"> 457 </span> <span class="FunctionName">_.isElement</span><span class="Keyword">:</span> <span class="FunctionArgument">obj</span> <span class="Storage">=&gt;</span> obj <span class="Keyword">and</span> obj.nodeType <span class="Keyword">is</span> <span class="Number">1</span>
<span class="line-numbers"> 458 </span>
<span class="line-numbers"> 459 </span>
<span class="line-numbers"> 460 </span> <span class="Comment"><span class="Comment">#</span> Is a given value an array?</span>
<span class="line-numbers"> 461 </span> <span class="FunctionName">_.isArray</span><span class="Keyword">:</span> <span class="FunctionArgument">obj</span> <span class="Storage">=&gt;</span> <span class="Keyword">!</span><span class="Keyword">!</span>(obj <span class="Keyword">and</span> obj.concat <span class="Keyword">and</span> obj.unshift)
<span class="line-numbers"> 462 </span>
<span class="line-numbers"> 463 </span>
<span class="line-numbers"> 464 </span> <span class="Comment"><span class="Comment">#</span> Is a given variable an arguments object?</span>
<span class="line-numbers"> 465 </span> <span class="FunctionName">_.isArguments</span><span class="Keyword">:</span> <span class="FunctionArgument">obj</span> <span class="Storage">=&gt;</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">obj</span> <span class="Storage">=&gt;</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">obj</span> <span class="Storage">=&gt;</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">obj</span> <span class="Storage">=&gt;</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">obj</span> <span class="Storage">=&gt;</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">obj</span> <span class="Storage">=&gt;</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">obj</span> <span class="Storage">=&gt;</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">obj</span> <span class="Storage">=&gt;</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">obj</span> <span class="Storage">=&gt;</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">=&gt;</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">value</span> <span class="Storage">=&gt;</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">=&gt;</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> 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">=&gt;</span>
<span class="line-numbers"> 522 </span> (prefix <span class="Keyword">or</span> <span class="String"><span class="String">'</span><span class="String">'</span></span>) <span class="Keyword">+</span> idCounter<span class="Keyword">++</span>
<span class="line-numbers"> 523 </span>
<span class="line-numbers"> 524 </span>
<span class="line-numbers"> 525 </span> <span class="Comment"><span class="Comment">#</span> JavaScript templating a-la ERB, pilfered from John Resig's</span>
<span class="line-numbers"> 526 </span> <span class="Comment"><span class="Comment">#</span> &quot;Secrets of the JavaScript Ninja&quot;, 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">=&gt;</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, &quot; &quot;).</span>
<span class="line-numbers"> 533 </span> <span class="String"> split(&quot;&lt;%&quot;).join(&quot;<span class="UserDefinedConstant">\t</span>&quot;).</span>
<span class="line-numbers"> 534 </span> <span class="String"> replace(/((^|%&gt;)[^<span class="UserDefinedConstant">\t</span>]*)'/g, &quot;$1<span class="UserDefinedConstant">\r</span>&quot;).</span>
<span class="line-numbers"> 535 </span> <span class="String"> replace(/<span class="UserDefinedConstant">\t</span>=(.*?)%&gt;/g, &quot;',$1,'&quot;).</span>
<span class="line-numbers"> 536 </span> <span class="String"> split(&quot;<span class="UserDefinedConstant">\t</span>&quot;).join(&quot;');&quot;).</span>
<span class="line-numbers"> 537 </span> <span class="String"> split(&quot;%&gt;&quot;).join(&quot;p.push('&quot;).</span>
<span class="line-numbers"> 538 </span> <span class="String"> split(&quot;<span class="UserDefinedConstant">\r</span>&quot;).join(&quot;<span class="UserDefinedConstant">\\</span>'&quot;) +</span>
<span class="line-numbers"> 539 </span> <span class="String"> &quot;');}return p.join('');&quot;)<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="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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</span> <span class="Variable">this</span>._wrapped
</pre>
</body>
</html>

View File

@@ -1,16 +1,16 @@
# Functions:
square: x => x * x.
square: x => x * x
sum: x, y => x + y.
sum: x, y => x + y
odd: x => x % 2 is 0.
odd: x => x % 2 is 0
even: x => x % 2 isnt 0.
even: x => x % 2 isnt 0
run_loop: =>
fire_events( e => e.stopPropagation(). )
fire_events(e => e.stopPropagation())
listen()
wait().
wait()
# Objects:
dense_object_literal: {one: 1, two: 2, three: 3}
@@ -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}]
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: []
@@ -45,9 +45,9 @@ if submarine.shields_up
else if submarine.sinking
abandon_ship()
else
run_away().
run_away()
eldest: if 25 > 21 then liz else marge.
eldest: if 25 > 21 then liz else marge
decoration: medal_of_honor if war_hero
@@ -58,8 +58,8 @@ race: =>
run()
walk()
crawl()
if tired then return sleep().
race().
if tired then return sleep()
race()
# Conditional assignment:
good ||= evil
@@ -81,54 +81,54 @@ try
dogs_and_cats_living_together()
throw "up"
catch error
print( error )
print(error)
finally
clean_up().
clean_up()
try all_hell_breaks_loose() catch error print(error) finally clean_up().
try all_hell_breaks_loose() catch error then print(error) finally clean_up()
# While loops, break and continue.
while demand > supply
sell()
restock().
restock()
while supply > demand then buy().
while supply > demand then buy()
while true
break if broken
continue if continuing.
continue if continuing
# Unary operators.
!!true
# Lexical scoping.
a: 5
v_1: 5
change_a_and_set_b: =>
a: 10
b: 15.
b: 20
v_1: 10
v_2: 15
v_2: 20
# Array comprehensions.
supper: food.capitalize() for food in ['toast', 'cheese', 'wine'].
supper: food.capitalize() for food in ['toast', 'cheese', 'wine']
drink(bottle) for bottle, i in ['soda', 'wine', 'lemonade'] if even(i).
drink(bottle) for bottle, i in ['soda', 'wine', 'lemonade'] when even(i)
# Switch statements ("else" serves as a default).
activity: switch day
when "Tuesday" then eat_breakfast()
when "Sunday" then go_to_church()
when "Saturday" then go_to_the_park()
when "Wednesday"
if day is bingo_day
go_to_bingo()
else
eat_breakfast()
go_to_work()
eat_dinner().
else go_to_work().
when "Tuesday" then eat_breakfast()
when "Sunday" then go_to_church()
when "Saturday" then go_to_the_park()
when "Wednesday"
if day is bingo_day
go_to_bingo()
else
eat_breakfast()
go_to_work()
eat_dinner()
else go_to_work()
# Semicolons can optionally be used instead of newlines.
wednesday: => eat_breakfast(); go_to_work(); eat_dinner(); .
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,21 +140,21 @@ sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad."
# Inheritance and calling super.
Animal: => .
Animal: =>
Animal.prototype.move: meters =>
alert(this.name + " moved " + meters + "m.").
alert(this.name + " moved " + meters + "m.")
Snake: name => this.name: name.
Snake: name => this.name: name
Snake extends Animal
Snake.prototype.move: =>
alert('Slithering...')
super(5).
super(5)
Horse: name => this.name: name.
Horse: name => this.name: name
Horse extends Animal
Horse.prototype.move: =>
alert('Galloping...')
super(45).
super(45)
sam: new Snake("Sammy the Snake")
tom: new Horse("Tommy the Horse")

View File

@@ -1,7 +1,7 @@
# Document Model
dc.model.Document: dc.Model.extend({
constructor: attributes => this.base(attributes).
constructor: attributes => this.base(attributes)
# For display, show either the highlighted search results, or the summary,
# if no highlights are available.
@@ -9,22 +9,22 @@ dc.model.Document: dc.Model.extend({
# version of the summary has all runs of whitespace squeezed out.
displaySummary: =>
text: this.get('highlight') or this.get('summary') or ''
text and text.replace(/\s+/g, ' ').
text and text.replace(/\s+/g, ' ')
# Return a list of the document's metadata. Think about caching this on the
# document by binding to Metadata, instead of on-the-fly.
metadata: =>
docId: this.id
_.select(Metadata.models()
meta => _.any(meta.get('instances')
instance => instance.document_id is docId.).).
_.select(Metadata.models(), (meta =>
_.any(meta.get('instances'), instance =>
instance.document_id is docId)))
bookmark: pageNumber =>
bookmark: new dc.model.Bookmark({title: this.get('title'), page_number: pageNumber, document_id: this.id})
Bookmarks.create(bookmark).
Bookmarks.create(bookmark)
# Inspect.
toString: => 'Document ' + this.id + ' "' + this.get('title') + '"'.
toString: => 'Document ' + this.id + ' "' + this.get('title') + '"'
})
@@ -37,31 +37,31 @@ dc.model.DocumentSet: dc.model.RESTfulSet.extend({
constructor: options =>
this.base(options)
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText').
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText')
selected: => _.select(this.models(), m => m.get('selected').).
selected: => _.select(this.models(), m => m.get('selected'))
selectedIds: => _.pluck(this.selected(), 'id').
selectedIds: => _.pluck(this.selected(), 'id')
countSelected: => this.selected().length.
countSelected: => this.selected().length
downloadSelectedViewers: =>
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip').
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip')
downloadSelectedPDF: =>
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('pdf_url')).
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip').
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('pdf_url'))
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip')
downloadSelectedFullText: =>
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('full_text_url')).
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip').
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('full_text_url'))
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip')
# We override "_onModelEvent" to fire selection changed events when documents
# change their selected state.
_onModelEvent: e, model =>
this.base(e, model)
fire: e == dc.Model.CHANGED and model.hasChanged('selected')
if fire then _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this))..
if fire then _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this))
})

View File

@@ -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,10 +14,10 @@
# end
LotteryTicket: {
get_picks: => this.picks.
set_picks: nums => this.picks: nums.
get_purchase: => this.purchase.
set_purchase: amount => this.purchase: amount.
get_picks: => this.picks
set_picks: nums => this.picks: nums
get_purchase: => this.purchase
set_purchase: amount => this.purchase: amount
}
@@ -33,8 +33,8 @@ LotteryTicket: {
WishScanner: {
scan_for_a_wish: =>
wish: this.read().detect( thought => thought.index('wish: ') is 0. )
wish.replace('wish: ', '').
wish: this.read().detect(thought => thought.index('wish: ') is 0)
wish.replace('wish: ', '')
}
@@ -79,28 +79,28 @@ Creature : {
# This method applies a hit taken during a fight.
hit: damage =>
p_up: Math.rand( this.charisma )
p_up: Math.rand(this.charisma)
if p_up % 9 is 7
this.life += p_up / 4
puts( "[" + this.name + " magick powers up " + p_up + "!]" ).
puts("[" + this.name + " magick powers up " + p_up + "!]")
this.life -= damage
if this.life <= 0 then puts( "[" + this.name + " has died.]" )..
if this.life <= 0 then puts("[" + this.name + " has died.]")
# This method takes one turn in a fight.
fight: enemy, weapon =>
if this.life <= 0 then return puts( "[" + this.name + "is too dead to fight!]" ).
if this.life <= 0 then return puts("[" + this.name + "is too dead to fight!]")
# Attack the opponent.
your_hit: Math.rand( this.strength + weapon )
puts( "[You hit with " + your_hit + "points of damage!]" )
enemy.hit( your_hit )
your_hit: Math.rand(this.strength + weapon)
puts("[You hit with " + your_hit + "points of damage!]")
enemy.hit(your_hit)
# Retaliation.
puts( enemy )
puts(enemy)
if enemy.life > 0
enemy_hit: Math.rand( enemy.strength + enemy.weapon )
puts( "[Your enemy hit with " + enemy_hit + "points of damage!]" )
this.hit( enemy_hit )..
enemy_hit: Math.rand(enemy.strength + enemy.weapon)
puts("[Your enemy hit with " + enemy_hit + "points of damage!]")
this.hit(enemy_hit)
}
@@ -123,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))
@@ -149,5 +149,5 @@ wipe_mutterings_from: sentence =>
while sentence.indexOf('(') >= 0
open: sentence.indexOf('(') - 1
close: sentence.indexOf(')') + 1
sentence: sentence[0..open] + sentence[close..sentence.length].
sentence.
sentence: sentence[0..open] + sentence[close..sentence.length]
sentence

View File

@@ -1,107 +1,126 @@
# Underscore.js
# (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
# Underscore is freely distributable under the terms of the MIT license.
# Portions of Underscore are inspired by or borrowed from Prototype.js,
# Oliver Steele's Functional, and John Resig's Micro-Templating.
# For all details and documentation:
# http://documentcloud.github.com/underscore/
# ------------------------- Baseline setup ---------------------------------
# Underscore.coffee
# (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
# Underscore is freely distributable under the terms of the MIT license.
# Portions of Underscore are inspired by or borrowed from Prototype.js,
# Oliver Steele's Functional, and John Resig's Micro-Templating.
# For all details and documentation:
# http://documentcloud.github.com/underscore/
# Establish the root object, "window" in the browser, or "global" on the server.
root: this
# Save the previous value of the "_" variable.
previousUnderscore: root._
# ------------------------- Baseline setup ---------------------------------
# If Underscore is called as a function, it returns a wrapped object that
# can be used OO-style. This wrapper holds altered versions of all the
# underscore functions. Wrapped objects may be chained.
wrapper: obj => this._wrapped: obj.
# Establish the root object, "window" in the browser, or "global" on the server.
root: this
# Establish the object that gets thrown to break out of a loop iteration.
breaker: if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration.
# Create a safe reference to the Underscore object for reference below.
_: root._: obj => new wrapper(obj).
# Save the previous value of the "_" variable.
previousUnderscore: root._
# Export the Underscore object for CommonJS.
if typeof(exports) != 'undefined' then exports._: _.
# Create quick reference variables for speed access to core prototypes.
slice: Array.prototype.slice
unshift: Array.prototype.unshift
toString: Object.prototype.toString
hasOwnProperty: Object.prototype.hasOwnProperty
propertyIsEnumerable: Object.prototype.propertyIsEnumerable
# If Underscore is called as a function, it returns a wrapped object that
# can be used OO-style. This wrapper holds altered versions of all the
# underscore functions. Wrapped objects may be chained.
wrapper: obj =>
this._wrapped: obj
this
# Current version.
_.VERSION: '0.5.1'
# ------------------------ Collection Functions: ---------------------------
# Establish the object that gets thrown to break out of a loop iteration.
breaker: if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
# The cornerstone, an each implementation.
# Handles objects implementing forEach, arrays, and raw objects.
_.each: obj, iterator, context =>
index: 0
try
return obj.forEach(iterator, context) if obj.forEach
return iterator.call(context, item, i, obj) for item, i in obj. if _.isArray(obj) or _.isArguments(obj)
iterator.call(context, obj[key], key, obj) for key in _.keys(obj).
catch e
throw e if e isnt breaker.
obj.
# Return the results of applying the iterator to each element. Use JavaScript
# 1.6's version of map, if possible.
_.map: obj, iterator, context =>
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
results: []
mapper: value, index, list => results.push(iterator.call(context, value, index, list)).
_.each(obj, mapper)
results.
# Create a safe reference to the Underscore object for reference below.
_: root._: obj => new wrapper(obj)
# Reduce builds up a single result from a list of values. Also known as
# inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
_.reduce: obj, memo, iterator, context =>
return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
reducer: value, index, list => memo: iterator.call(context, memo, value, index, list).
_.each(obj, reducer)
memo.
# The right-associative version of reduce, also known as foldr. Uses
# JavaScript 1.8's version of reduceRight, if available.
_.reduceRight: obj, memo, iterator, context =>
return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
reversed: _.clone(_.toArray(obj)).reverse()
reverser: value, index => memo: iterator.call(context, memo, value, index, obj).
_.each(reversed, reverser)
memo.
# Export the Underscore object for CommonJS.
if typeof(exports) != 'undefined' then exports._: _
# Create quick reference variables for speed access to core prototypes.
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.3'
# ------------------------ Collection Functions: ---------------------------
# The cornerstone, an each implementation.
# Handles objects implementing forEach, arrays, and raw objects.
_.each: obj, iterator, context =>
index: 0
try
return obj.forEach(iterator, context) if obj.forEach
if _.isArray(obj) or _.isArguments(obj)
return iterator.call(context, obj[i], i, obj) for i in [0...obj.length]
iterator.call(context, val, key, obj) for val, key in obj
catch e
throw e if e isnt breaker
obj
# Return the results of applying the iterator to each element. Use JavaScript
# 1.6's version of map, if possible.
_.map: obj, iterator, context =>
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
results: []
_.each(obj) value, index, list =>
results.push(iterator.call(context, value, index, list))
results
# Reduce builds up a single result from a list of values. Also known as
# inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
_.reduce: obj, memo, iterator, context =>
return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
_.each(obj) value, index, list =>
memo: iterator.call(context, memo, value, index, list)
memo
# The right-associative version of reduce, also known as foldr. Uses
# JavaScript 1.8's version of reduceRight, if available.
_.reduceRight: obj, memo, iterator, context =>
return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
_.each(_.clone(_.toArray(obj)).reverse()) value, index =>
memo: iterator.call(context, memo, value, index, obj)
memo
# Return the first value which passes a truth test.
_.detect: obj, iterator, context =>
result: null
_.each(obj, (value, index, list =>
_.each(obj) value, index, list =>
if iterator.call(context, value, index, list)
result: value
_.breakLoop()..))
result.
_.breakLoop()
result
# Return all the elements that pass a truth test. Use JavaScript 1.6's
# filter(), if it exists.
_.select: obj, iterator, context =>
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context).
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context)
results: []
_.each(obj, (value, index, list =>
results.push(value) if iterator.call(context, value, index, list).))
results.
_.each(obj) value, index, list =>
results.push(value) if iterator.call(context, value, index, list)
results
# Return all the elements for which a truth test fails.
_.reject: obj, iterator, context =>
results: []
_.each(obj, (value, index, list =>
results.push(value) if not iterator.call(context, value, index, list).))
results.
_.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.
@@ -109,9 +128,10 @@ _.reduceRight: obj, memo, iterator, context =>
iterator ||= _.identity
return obj.every(iterator, context) if obj and _.isFunction(obj.every)
result: true
_.each(obj, (value, index, list =>
_.breakLoop() unless result: result and iterator.call(context, value, index, list).))
result.
_.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.
@@ -119,193 +139,193 @@ _.reduceRight: obj, memo, iterator, context =>
iterator ||= _.identity
return obj.some(iterator, context) if obj and _.isFunction(obj.some)
result: false
_.each(obj, (value, index, list =>
_.breakLoop() if (result: iterator.call(context, value, index, list)).))
result.
_.each(obj) value, index, list =>
_.breakLoop() if (result: iterator.call(context, value, index, list))
result
# Determine if a given value is included in the array or object,
# based on '==='.
_.include: obj, target =>
return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
found: false
_.each(obj, (value =>
_.breakLoop() if (found: value is target).))
found.
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 =>
args: _.rest(arguments, 2)
_.map(obj, (value =>
(if method then value[method] else value.).apply(value, args).)).
(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, (value => value[key].)).
_.map(obj, (val => val[key]))
# Return the maximum item or (item-based computation).
_.max: obj, iterator, context =>
return Math.max.apply(Math, obj) if !iterator and _.isArray(obj)
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
result: {computed: -Infinity}
_.each(obj, (value, index, list =>
computed: if iterator then iterator.call(context, value, index, list) else value.
computed >= result.computed and (result: {value: value, computed: computed}).))
result.value.
#
# # Return the minimum element (or element-based computation).
# _.min = function(obj, iterator, context) {
# if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
# var result = {computed : Infinity};
# _.each(obj, function(value, index, list) {
# var computed = iterator ? iterator.call(context, value, index, list) : value;
# computed < result.computed && (result = {value : value, computed : computed});
# });
# return result.value;
# };
#
# # Sort the object's values by a criteria produced by an iterator.
# _.sortBy = function(obj, iterator, context) {
# return _.pluck(_.map(obj, function(value, index, list) {
# return {
# value : value,
# criteria : iterator.call(context, value, index, list)
# };
# }).sort(function(left, right) {
# var a = left.criteria, b = right.criteria;
# return a < b ? -1 : a > b ? 1 : 0;
# }), 'value');
# };
#
# # Use a comparator function to figure out at what index an object should
# # be inserted so as to maintain order. Uses binary search.
# _.sortedIndex = function(array, obj, iterator) {
# iterator = iterator || _.identity;
# var low = 0, high = array.length;
# while (low < high) {
# var mid = (low + high) >> 1;
# iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
# }
# return low;
# };
#
# # Convert anything iterable into a real, live array.
# _.toArray = function(iterable) {
# if (!iterable) return [];
# if (iterable.toArray) return iterable.toArray();
# if (_.isArray(iterable)) return iterable;
# if (_.isArguments(iterable)) return slice.call(iterable);
# return _.map(iterable, function(val){ return val; });
# };
#
# # Return the number of elements in an object.
# _.size = function(obj) {
# return _.toArray(obj).length;
# };
#
# /*-------------------------- Array Functions: ------------------------------*/
#
# # Get the first element of an array. Passing "n" will return the first N
# # values in the array. Aliased as "head". The "guard" check allows it to work
# # with _.map.
# _.first = function(array, n, guard) {
# return n && !guard ? slice.call(array, 0, n) : array[0];
# };
#
# # Returns everything but the first entry of the array. Aliased as "tail".
# # Especially useful on the arguments object. Passing an "index" will return
# # the rest of the values in the array from that index onward. The "guard"
# //check allows it to work with _.map.
# _.rest = function(array, index, guard) {
# return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
# };
#
# # Get the last element of an array.
# _.last = function(array) {
# return array[array.length - 1];
# };
#
# # Trim out all falsy values from an array.
# _.compact = function(array) {
# return _.select(array, function(value){ return !!value; });
# };
#
# # Return a completely flattened version of an array.
# _.flatten = function(array) {
# return _.reduce(array, [], function(memo, value) {
# if (_.isArray(value)) return memo.concat(_.flatten(value));
# memo.push(value);
# return memo;
# });
# };
#
# # Return a version of the array that does not contain the specified value(s).
# _.without = function(array) {
# var values = _.rest(arguments);
# return _.select(array, function(value){ return !_.include(values, value); });
# };
#
# # 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 = function(array, isSorted) {
# return _.reduce(array, [], function(memo, el, i) {
# if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
# return memo;
# });
# };
#
# # Produce an array that contains every item shared between all the
# # passed-in arrays.
# _.intersect = function(array) {
# var rest = _.rest(arguments);
# return _.select(_.uniq(array), function(item) {
# return _.all(rest, function(other) {
# return _.indexOf(other, item) >= 0;
# });
# });
# };
#
# # Zip together multiple lists into a single array -- elements that share
# # an index go together.
# _.zip = function() {
# var args = _.toArray(arguments);
# var length = _.max(_.pluck(args, 'length'));
# var results = new Array(length);
# for (var i=0; i<length; i++) results[i] = _.pluck(args, String(i));
# return 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 = function(array, item) {
# if (array.indexOf) return array.indexOf(item);
# for (var i=0, l=array.length; i<l; i++) if (array[i] === item) return i;
# return -1;
# };
#
# # Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
# # if possible.
# _.lastIndexOf = function(array, item) {
# if (array.lastIndexOf) return array.lastIndexOf(item);
# var i = array.length;
# while (i--) if (array[i] === item) return i;
# return -1;
# };
#
# # Generate an integer Array containing an arithmetic progression. A port of
# # the native Python range() function. See:
# # http://docs.python.org/library/functions.html#range
# _.range = function(start, stop, step) {
# var a = _.toArray(arguments);
# var solo = a.length <= 1;
# var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1;
# var len = Math.ceil((stop - start) / step);
# if (len <= 0) return [];
# var range = new Array(len);
# for (var i = start, idx = 0; true; i += step) {
# if ((step > 0 ? i - stop : stop - i) >= 0) return range;
# range[idx++] = i;
# }
# };
_.each(obj) value, index, list =>
computed: if iterator then iterator.call(context, value, index, list) else value
computed >= result.computed and (result: {value: value, computed: computed})
result.value
# Return the minimum element (or element-based computation).
_.min: obj, iterator, context =>
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
result: {computed: Infinity}
_.each(obj) value, index, list =>
computed: if iterator then iterator.call(context, value, index, list) else value
computed < result.computed and (result: {value: value, computed: computed})
result.value
# Sort the object's values by a criteria produced by an iterator.
_.sortBy: obj, iterator, context =>
_.pluck(((_.map(obj) value, index, list =>
{value: value, criteria: iterator.call(context, value, index, list)}
).sort() left, right =>
a: left.criteria; b: right.criteria
if a < b then -1 else if a > b then 1 else 0
), 'value')
# Use a comparator function to figure out at what index an object should
# be inserted so as to maintain order. Uses binary search.
_.sortedIndex: array, obj, iterator =>
iterator ||= _.identity
low: 0; high: array.length
while low < high
mid: (low + high) >> 1
if iterator(array[mid]) < iterator(obj) then low: mid + 1 else high: mid
low
# Convert anything iterable into a real, live array.
_.toArray: iterable =>
return [] if (!iterable)
return iterable.toArray() if (iterable.toArray)
return iterable if (_.isArray(iterable))
return slice.call(iterable) if (_.isArguments(iterable))
_.values(iterable)
# Return the number of elements in an object.
_.size: obj => _.toArray(obj).length
# -------------------------- Array Functions: ------------------------------
# Get the first element of an array. Passing "n" will return the first N
# values in the array. Aliased as "head". The "guard" check allows it to work
# with _.map.
_.first: array, n, guard =>
if n and not guard then slice.call(array, 0, n) else array[0]
# Returns everything but the first entry of the array. Aliased as "tail".
# Especially useful on the arguments object. Passing an "index" will return
# the rest of the values in the array from that index onward. The "guard"
# check allows it to work with _.map.
_.rest: array, index, guard =>
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
# Get the last element of an array.
_.last: array => array[array.length - 1]
# Trim out all falsy values from an array.
_.compact: array => array[i] for i in [0...array.length] when array[i]
# Return a completely flattened version of an array.
_.flatten: array =>
_.reduce(array, []) memo, value =>
return memo.concat(_.flatten(value)) if _.isArray(value)
memo.push(value)
memo
# Return a version of the array that does not contain the specified value(s).
_.without: array =>
values: _.rest(arguments)
val for val in _.toArray(array) when not _.include(values, val)
# Produce a duplicate-free version of the array. If the array has already
# been sorted, you have the option of using a faster algorithm.
_.uniq: array, isSorted =>
memo: []
for el, i in _.toArray(array)
memo.push(el) if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
memo
# Produce an array that contains every item shared between all the
# passed-in arrays.
_.intersect: array =>
rest: _.rest(arguments)
_.select(_.uniq(array)) item =>
_.all(rest) other =>
_.indexOf(other, item) >= 0
# Zip together multiple lists into a single array -- elements that share
# an index go together.
_.zip: =>
args: _.toArray(arguments)
length: _.max(_.pluck(args, 'length'))
results: new Array(length)
for i in [0...length]
results[i]: _.pluck(args, String(i))
results
# If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
# we need this function. Return the position of the first occurence of an
# item in an array, or -1 if the item is not included in the array.
_.indexOf: array, item =>
return array.indexOf(item) if array.indexOf
i: 0; l: array.length
while l - i
if array[i] is item then return i else i++
-1
# Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
# if possible.
_.lastIndexOf: array, item =>
return array.lastIndexOf(item) if array.lastIndexOf
i: array.length
while i
if array[i] is item then return i else i--
-1
# Generate an integer Array containing an arithmetic progression. A port of
# the native Python range() function. See:
# http://docs.python.org/library/functions.html#range
_.range: start, stop, step =>
a: _.toArray(arguments)
solo: a.length <= 1
i: start: if solo then 0 else a[0];
stop: if solo then a[0] else a[1];
step: a[2] or 1
len: Math.ceil((stop - start) / step)
return [] if len <= 0
range: new Array(len)
idx: 0
while true
return range if (if step > 0 then i - stop else stop - i) >= 0
range[idx]: i
idx++
i+= step
# ----------------------- Function Functions: -----------------------------
@@ -313,34 +333,36 @@ _.reduceRight: obj, memo, iterator, context =>
# optionally). Binding with arguments is also known as 'curry'.
_.bind: func, obj =>
args: _.rest(arguments, 2)
=> func.apply(obj or root, args.concat(_.toArray(arguments)))..
=> func.apply(obj or root, args.concat(_.toArray(arguments)))
# Bind all of an object's methods to that object. Useful for ensuring that
# all callbacks defined on an object belong to it.
_.bindAll: obj =>
funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
_.each(funcs, (f => obj[f]: _.bind(obj[f], obj)))
obj
# Delays a function for the given number of milliseconds, and then calls
# it with the arguments supplied.
_.delay: func, wait =>
args: _.rest(arguments, 2)
setTimeout((=> func.apply(func, args)), wait)
# # Bind all of an object's methods to that object. Useful for ensuring that
# # all callbacks defined on an object belong to it.
# _.bindAll = function(obj) {
# var funcs = _.rest(arguments);
# if (funcs.length == 0) funcs = _.functions(obj);
# _.each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
# return obj;
# };
#
# # Delays a function for the given number of milliseconds, and then calls
# # it with the arguments supplied.
# _.delay = function(func, wait) {
# var args = _.rest(arguments, 2);
# return setTimeout(function(){ return func.apply(func, args); }, wait);
# };
# Defers a function, scheduling it to run after the current call stack has
# cleared.
_.defer: func =>
_.delay.apply(_, [func, 1].concat(_.rest(arguments))).
_.delay.apply(_, [func, 1].concat(_.rest(arguments)))
# Returns the first function passed as an argument to the second,
# allowing you to adjust arguments, run code before and after, and
# conditionally execute the original function.
_.wrap: func, wrapper =>
=> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))..
=> 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.
@@ -348,40 +370,48 @@ _.reduceRight: obj, memo, iterator, context =>
funcs: _.toArray(arguments)
=>
args: _.toArray(arguments)
args: [funcs[i]].apply(this, args) for i in [(funcs.length - 1)..0].
args[0]..
for i in [(funcs.length - 1)..0]
args: [funcs[i].apply(this, args)]
args[0]
# ------------------------- Object Functions: ----------------------------
# Retrieve the names of an object's properties.
_.keys: obj =>
return _.range(0, obj.length) if _.isArray(obj)
key for val, key in obj
# Retrieve the values of an object's properties.
_.values: obj =>
_.map(obj, _.identity)
# Return a sorted list of the function names available in Underscore.
_.functions: obj =>
_.select(_.keys(obj), key => _.isFunction(obj[key])).sort()
# Extend a given object with all of the properties in a source object.
_.extend: destination, source =>
for val, key in source
destination[key]: val
destination
# Create a (shallow-cloned) duplicate of an object.
_.clone: obj =>
return obj.slice(0) if _.isArray(obj)
_.extend({}, obj)
# Invokes interceptor with the obj, and then returns obj.
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
_.tap: obj, interceptor =>
interceptor(obj)
obj
# /* ------------------------- Object Functions: ---------------------------- */
#
# # Retrieve the names of an object's properties.
# _.keys = function(obj) {
# if(_.isArray(obj)) return _.range(0, obj.length);
# var keys = [];
# for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
# return keys;
# };
#
# # Retrieve the values of an object's properties.
# _.values = function(obj) {
# return _.map(obj, _.identity);
# };
#
# # Return a sorted list of the function names available in Underscore.
# _.functions = function(obj) {
# return _.select(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
# };
#
# # Extend a given object with all of the properties in a source object.
# _.extend = function(destination, source) {
# for (var property in source) destination[property] = source[property];
# return destination;
# };
#
# # Create a (shallow-cloned) duplicate of an object.
# _.clone = function(obj) {
# if (_.isArray(obj)) return obj.slice(0);
# return _.extend({}, obj);
# };
# Perform a deep comparison to check if two objects are equal.
_.isEqual: a, b =>
@@ -402,10 +432,10 @@ _.reduceRight: obj, memo, iterator, context =>
return true if _.isNaN(a) and _.isNaN(b)
# Compare regular expressions.
if _.isRegExp(a) and _.isRegExp(b)
return a.source is b.source and \
a.global is b.global and \
a.ignoreCase is b.ignoreCase and \
a.multiline is b.multiline.
return a.source is b.source and
a.global is b.global and
a.ignoreCase is b.ignoreCase and
a.multiline is b.multiline
# If a is not an object by this point, we can't handle it.
return false if atype isnt 'object'
# Check for different array lengths before comparing contents.
@@ -416,42 +446,57 @@ _.reduceRight: obj, memo, iterator, context =>
return false if aKeys.length isnt bKeys.length
# Recursive comparison of contents.
# for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
return true.
return true
# 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)
# Is a given variable an arguments object?
_.isArguments: obj => obj and _.isNumber(obj.length) and !_.isArray(obj) and !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)
# Is the given value a string?
_.isString: obj => !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
# Is a given value a number?
_.isNumber: obj => toString.call(obj) is '[object Number]'
# Is a given value a Date?
_.isDate: obj => !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
# Is the given value a regular expression?
_.isRegExp: obj => !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
# Is the given value NaN -- this one is interesting. NaN != NaN, and
# isNaN(undefined) == true, so we make sure it's a number first.
_.isNaN: obj => _.isNumber(obj) and 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'
# Invokes interceptor with the obj, and then returns obj.
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
_.tap: obj, interceptor =>
interceptor(obj)
obj.
# # Define the isArray, isDate, isFunction, isNumber, isRegExp, and isString
# # functions based on their toString identifiers.
# var types = ['Array', 'Date', 'Function', 'Number', 'RegExp', 'String'];
# for (var i=0, l=types.length; i<l; i++) {
# (function() {
# var identifier = '[object ' + types[i] + ']';
# _['is' + types[i]] = function(obj) { return toString.call(obj) == identifier; };
# })();
# }
# -------------------------- Utility Functions: --------------------------
@@ -459,39 +504,41 @@ _.reduceRight: obj, memo, iterator, context =>
# previous owner. Returns a reference to the Underscore object.
_.noConflict: =>
root._: previousUnderscore
this.
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 =>
(prefix or '') + idCounter++
# JavaScript templating a-la ERB, pilfered from John Resig's
# "Secrets of the JavaScript Ninja", page 83.
_.template: str, data =>
`var fn = new Function('obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj){p.push(\'' +
str.
replace(/[\r\t\n]/g, " ").
split("<%").join("\t").
replace(/((^|%>)[^\t]*)'/g, "$1\r").
replace(/\t=(.*?)%>/g, "',$1,'").
split("\t").join("');").
split("%>").join("p.push('").
split("\r").join("\\'") +
"');}return p.join('');")`
if data then fn(data) else fn
# # Generate a unique integer id (unique within the entire client session).
# # Useful for temporary DOM ids.
# var idCounter = 0;
# _.uniqueId = function(prefix) {
# var id = idCounter++;
# return prefix ? prefix + id : id;
# };
#
# # JavaScript templating a-la ERB, pilfered from John Resig's
# # "Secrets of the JavaScript Ninja", page 83.
# _.template = function(str, data) {
# var fn = new Function('obj',
# 'var p=[],print=function(){p.push.apply(p,arguments);};' +
# 'with(obj){p.push(\'' +
# str
# .replace(/[\r\t\n]/g, " ")
# .split("<%").join("\t")
# .replace(/((^|%>)[^\t]*)'/g, "$1\r")
# .replace(/\t=(.*?)%>/g, "',$1,'")
# .split("\t").join("');")
# .split("%>").join("p.push('")
# .split("\r").join("\\'")
# + "');}return p.join('');");
# return data ? fn(data) : fn;
# };
# ------------------------------- Aliases ----------------------------------
@@ -505,48 +552,42 @@ _.reduceRight: obj, memo, iterator, context =>
_.tail: _.rest
_.methods: _.functions
# /*------------------------ Setup the OOP Wrapper: --------------------------*/
#
# # Helper function to continue chaining intermediate results.
# var result = function(obj, chain) {
# return chain ? _(obj).chain() : obj;
# };
#
# # Add all of the Underscore functions to the wrapper object.
# _.each(_.functions(_), function(name) {
# var method = _[name];
# wrapper.prototype[name] = function() {
# unshift.call(arguments, this._wrapped);
# return result(method.apply(_, arguments), this._chain);
# };
# });
#
# # Add all mutator Array functions to the wrapper.
# _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
# var method = Array.prototype[name];
# wrapper.prototype[name] = function() {
# method.apply(this._wrapped, arguments);
# return result(this._wrapped, this._chain);
# };
# });
#
# # Add all accessor Array functions to the wrapper.
# _.each(['concat', 'join', 'slice'], function(name) {
# var method = Array.prototype[name];
# wrapper.prototype[name] = function() {
# return result(method.apply(this._wrapped, arguments), this._chain);
# };
# });
#
# # Start chaining a wrapped Underscore object.
# wrapper.prototype.chain = function() {
# this._chain = true;
# return this;
# };
#
# # Extracts the result from a wrapped and chained object.
# wrapper.prototype.value = function() {
# return this._wrapped;
# };
#
# ()
# /*------------------------ Setup the OOP Wrapper: --------------------------*/
# Helper function to continue chaining intermediate results.
result: obj, chain =>
if chain then _(obj).chain() else obj
# Add all of the Underscore functions to the wrapper object.
_.each(_.functions(_)) name =>
method: _[name]
wrapper.prototype[name]: =>
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 =>
method: Array.prototype[name]
wrapper.prototype[name]: =>
method.apply(this._wrapped, arguments)
result(this._wrapped, this._chain)
# Add all accessor Array functions to the wrapper.
_.each(['concat', 'join', 'slice']) name =>
method: Array.prototype[name]
wrapper.prototype[name]: =>
result(method.apply(this._wrapped, arguments), this._chain)
# Start chaining a wrapped Underscore object.
wrapper.prototype.chain: =>
this._chain: true
this
# Extracts the result from a wrapped and chained object.
wrapper.prototype.value: => this._wrapped

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,13 @@ require "coffee_script/parser"
require "coffee_script/nodes"
require "coffee_script/value"
require "coffee_script/scope"
require "coffee_script/rewriter"
require "coffee_script/parse_error"
# Namespace for all CoffeeScript internal classes.
module CoffeeScript
VERSION = '0.1.6' # 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={})

View File

@@ -39,7 +39,7 @@
<key>comment</key>
<string>match stuff like: funcName: =&gt; … </string>
<key>match</key>
<string>([a-zA-Z_?.$]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</string>
<string>([a-zA-Z0-9_?.$*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=&gt;)</string>
<key>name</key>
<string>meta.function.coffee</string>
</dict>
@@ -60,7 +60,7 @@
<key>comment</key>
<string>match stuff like: a =&gt; … </string>
<key>match</key>
<string>([a-zA-Z_?., $]*)\s*(=&gt;)</string>
<string>([a-zA-Z0-9_?., $*]*)\s*(=&gt;)</string>
<key>name</key>
<string>meta.inline.function.coffee</string>
</dict>
@@ -204,10 +204,29 @@
</dict>
<dict>
<key>match</key>
<string>\b(break|when|catch|continue|else|finally|for|if|return|switch|then|throw|try|unless|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>
<key>name</key>
<string>variable.assignment.coffee</string>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>entity.name.function.coffee</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>keyword.operator.coffee</string>
</dict>
</dict>
</dict>
<dict>
<key>match</key>
<string>\b(true|on|yes)\b</string>
@@ -240,7 +259,7 @@
</dict>
<dict>
<key>match</key>
<string>!|\$|%|&amp;|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
<string>!|\$|%|&amp;|\*|\/|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|&lt;=|&gt;=|&lt;&lt;=|&gt;&gt;=|&gt;&gt;&gt;=|&lt;&gt;|&lt;|&gt;|!|&amp;&amp;|\?|\|\||\:|\*=|(?&lt;!\()/=|%=|\+=|\-=|&amp;=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
<key>name</key>
<string>keyword.operator.coffee</string>
</dict>

View File

@@ -19,8 +19,9 @@ Usage:
# Seconds to pause between checks for changed source files.
WATCH_INTERVAL = 0.5
# Path to the Narwhal Launcher:
LAUNCHER = File.expand_path(File.dirname(__FILE__)) + '/narwhal/js/launcher.js'
# 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
@@ -107,7 +108,7 @@ Usage:
# Use Narwhal to run an interactive CoffeeScript session.
def launch_repl
exec "narwhal #{LAUNCHER}"
exec "#{LAUNCHER}"
rescue Errno::ENOENT
puts "Error: Narwhal must be installed to use the interactive REPL."
exit(1)
@@ -116,7 +117,7 @@ Usage:
# Use Narwhal to compile and execute CoffeeScripts.
def run_scripts
sources = @sources.join(' ')
exec "narwhal #{LAUNCHER} #{sources}"
exec "#{LAUNCHER} #{sources}"
rescue Errno::ENOENT
puts "Error: Narwhal must be installed in order to execute CoffeeScripts."
exit(1)

View File

@@ -1,24 +1,26 @@
class Parser
# Declare tokens produced by the lexer
token IF ELSE THEN UNLESS
token IF ELSE UNLESS
token NUMBER STRING REGEX
token TRUE FALSE YES NO ON OFF
token IDENTIFIER PROPERTY_ACCESS
token CODE PARAM NEW RETURN
token CODE PARAM PARAM_SPLAT NEW RETURN
token TRY CATCH FINALLY THROW
token BREAK CONTINUE
token FOR IN WHILE
token SWITCH WHEN
token FOR IN BY WHEN WHILE
token SWITCH LEADING_WHEN
token DELETE INSTANCEOF TYPEOF
token SUPER EXTENDS
token NEWLINE
token COMMENT
token JS
token INDENT OUTDENT
# Declare order of operations.
prechigh
nonassoc UMINUS NOT '!' '!!' '~' '++' '--'
left '?'
nonassoc UMINUS PARAM_SPLAT SPLAT NOT '!' '!!' '~' '++' '--'
left '*' '/' '%'
left '+' '-'
left '<<' '>>' '>>>'
@@ -29,51 +31,40 @@ prechigh
right '-=' '+=' '/=' '*=' '%='
right DELETE INSTANCEOF TYPEOF
left '.'
right THROW FOR IN WHILE NEW SUPER
left UNLESS IF ELSE EXTENDS
right INDENT
left OUTDENT
right WHEN LEADING_WHEN IN BY
right THROW FOR NEW SUPER
left EXTENDS
left ASSIGN '||=' '&&='
right RETURN
right RETURN '=>' UNLESS IF ELSE WHILE
preclow
# We expect 3 shift/reduce errors for optional syntax.
# There used to be 252 -- greatly improved.
expect 3
rule
# All parsing will end in this rule, being the trunk of the AST.
Root:
/* nothing */ { result = Expressions.new([]) }
| Terminator { result = Expressions.new([]) }
/* nothing */ { result = Expressions.new }
| Terminator { result = Expressions.new }
| Expressions { result = val[0] }
| Block Terminator { result = val[0] }
;
# Any list of expressions or method body, seperated by line breaks or semis.
Expressions:
Expression { result = Expressions.new(val) }
Expression { result = Expressions.wrap(val) }
| Expressions Terminator Expression { result = val[0] << val[2] }
| Expressions Terminator { result = val[0] }
| Terminator Expressions { result = val[1] }
;
# All types of expressions in our language.
Expression:
PureExpression
| Statement
;
# The parts that are natural JavaScript expressions.
PureExpression:
Value
| Call
| Code
| Operation
| Range
;
# We have to take extra care to convert these statements into expressions.
Statement:
Assign
| Assign
| If
| Try
| Throw
@@ -82,21 +73,22 @@ rule
| For
| Switch
| Extends
| Splat
| Existence
| Comment
;
Block:
INDENT Expressions OUTDENT { result = val[1] }
| INDENT OUTDENT { result = Expressions.new }
;
# All tokens that can terminate an expression.
Terminator:
"\n"
| ";"
;
# All tokens that can serve to begin the second block of a multi-part expression.
Then:
THEN
| Terminator
;
# All hard-coded values.
Literal:
NUMBER { result = LiteralNode.new(val[0]) }
@@ -115,13 +107,13 @@ rule
# Assignment to a variable.
Assign:
Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
;
# Assignment within an object literal.
AssignObj:
IDENTIFIER 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) }
IDENTIFIER ASSIGN Expression { result = AssignNode.new(ValueNode.new(val[0]), val[2], :object) }
| STRING ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
| Comment { result = val[0] }
;
@@ -146,6 +138,8 @@ rule
| '~' Expression { result = OpNode.new(val[0], val[1]) }
| '--' Expression { result = OpNode.new(val[0], val[1]) }
| '++' Expression { result = OpNode.new(val[0], val[1]) }
| DELETE Expression { result = OpNode.new(val[0], val[1]) }
| TYPEOF Expression { result = OpNode.new(val[0], val[1]) }
| Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
| Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
@@ -187,27 +181,32 @@ rule
| Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| DELETE Expression { result = OpNode.new(val[0], val[1]) }
| TYPEOF Expression { result = OpNode.new(val[0], val[1]) }
| Expression INSTANCEOF Expression { result = OpNode.new(val[1], val[0], val[2]) }
;
Existence:
Expression '?' { result = ExistenceNode.new(val[0]) }
;
# Function definition.
Code:
ParamList "=>" CodeBody "." { result = CodeNode.new(val[0], val[2]) }
| "=>" CodeBody "." { result = CodeNode.new([], val[1]) }
;
# The body of a function.
CodeBody:
/* nothing */ { result = Expressions.new([]) }
| Expressions { result = val[0] }
ParamList "=>" Block { result = CodeNode.new(val[0], val[2]) }
| "=>" Block { result = CodeNode.new([], val[1]) }
;
# The parameters to a function definition.
ParamList:
PARAM { result = val }
| ParamList "," PARAM { result = val[0] << val[2] }
Param { result = val }
| ParamList "," Param { result = val[0] << val[2] }
;
Param:
PARAM
| PARAM_SPLAT PARAM { result = ParamSplatNode.new(val[1]) }
;
Splat:
'*' Value = SPLAT { result = ArgSplatNode.new(val[1]) }
;
# Expressions that can be treated as values.
@@ -240,10 +239,13 @@ rule
# Assignment within an object literal (comma or newline separated).
AssignList:
/* nothing */ { result = []}
/* nothing */ { result = [] }
| AssignObj { result = val }
| AssignList "," AssignObj { result = val[0] << val[2] }
| AssignList Terminator AssignObj { result = val[0] << val[2] }
| AssignList ","
Terminator AssignObj { result = val[0] << val[3] }
| INDENT AssignList OUTDENT { result = val[1] }
;
# All flavors of function call (instantiation, super, and regular).
@@ -260,8 +262,14 @@ rule
# A generic function invocation.
Invocation:
Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
| Invocation "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
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] }
;
Arguments:
"(" ArgList ")" { result = val[1] }
| "(" ArgList ")" Code { result = val[1] << val[3] }
;
# Calling super.
@@ -271,8 +279,10 @@ rule
# The range literal.
Range:
"[" Value "." "." Value "]" { result = RangeNode.new(val[1], val[4]) }
| "[" Value "." "." "." Value "]" { result = RangeNode.new(val[1], val[5], true) }
"[" Expression
"." "." Expression "]" { result = RangeNode.new(val[1], val[4]) }
| "[" Expression
"." "." "." Expression "]" { result = RangeNode.new(val[1], val[5], true) }
;
# The array literal.
@@ -284,21 +294,25 @@ rule
ArgList:
/* nothing */ { result = [] }
| Expression { result = val }
| INDENT Expression { result = [val[1]] }
| ArgList "," Expression { result = val[0] << val[2] }
| ArgList Terminator Expression { result = val[0] << val[2] }
| ArgList "," Terminator Expression { result = val[0] << val[3] }
| ArgList "," INDENT Expression { result = val[0] << val[3] }
| ArgList OUTDENT { result = val[0] }
;
# Try/catch/finally exception handling blocks.
Try:
TRY Expressions Catch "." { result = TryNode.new(val[1], val[2][0], val[2][1]) }
| TRY Expressions Catch
FINALLY Expressions "." { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) }
TRY Block Catch { result = TryNode.new(val[1], val[2][0], val[2][1]) }
| TRY Block FINALLY Block { result = TryNode.new(val[1], nil, nil, val[3]) }
| TRY Block Catch
FINALLY Block { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) }
;
# A catch clause.
Catch:
/* nothing */ { result = [nil, nil] }
| CATCH IDENTIFIER Expressions { result = [val[1], val[2]] }
CATCH IDENTIFIER Block { result = [val[1], val[2]] }
;
# Throw an exception.
@@ -308,20 +322,20 @@ rule
# Parenthetical expressions.
Parenthetical:
"(" Expressions ")" { result = ParentheticalNode.new(val[1]) }
"(" Expression ")" { result = ParentheticalNode.new(val[1], val[0].line) }
;
# The while loop. (there is no do..while).
While:
WHILE Expression Then
Expressions "." { result = WhileNode.new(val[1], val[3]) }
WHILE Expression Block { result = WhileNode.new(val[1], val[2]) }
;
# Array comprehensions, including guard and current index.
# Looks a little confusing, check nodes.rb for the arguments to ForNode.
For:
Expression FOR
ForVariables ForSource { result = ForNode.new(val[0], val[3][0], val[2][0], val[3][1], val[2][1]) }
ForVariables ForSource { result = ForNode.new(val[0], val[3], val[2][0], val[2][1]) }
| FOR ForVariables ForSource Block { result = ForNode.new(val[3], val[2], val[1][0], val[1][1]) }
;
# An array comprehension has variables for the current element and index.
@@ -332,17 +346,19 @@ rule
# The source of the array comprehension can optionally be filtered.
ForSource:
IN PureExpression "." { result = [val[1]] }
| IN PureExpression
IF Expression "." { result = [val[1], val[3]] }
IN Expression { result = {:source => val[1]} }
| ForSource
WHEN Expression { result = val[0].merge(:filter => val[2]) }
| ForSource
BY Expression { result = val[0].merge(:step => val[2]) }
;
# Switch/When blocks.
Switch:
SWITCH Expression Then
Whens "." { result = val[3].rewrite_condition(val[1]) }
| SWITCH Expression Then
Whens ELSE Expressions "." { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
SWITCH Expression INDENT
Whens OUTDENT { result = val[3].rewrite_condition(val[1]) }
| SWITCH Expression INDENT
Whens ELSE Block OUTDENT { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
;
# The inner list of whens.
@@ -353,16 +369,22 @@ rule
# An individual when.
When:
WHEN Expression Then Expressions { result = IfNode.new(val[1], val[3]) }
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
;
# 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]) }
;
# An elsif portion of an if-else block.
ElsIf:
ELSE IF Expression
Then Expressions { result = IfNode.new(val[2], val[4]) }
ELSE IfBlock { result = val[1].force_statement }
;
# Multiple elsifs can be chained together.
@@ -373,8 +395,8 @@ rule
# Terminating else bodies are strictly optional.
ElseBody
"." { result = nil }
| ELSE Expressions "." { result = val[1] }
/* nothing */ { result = nil }
| ELSE Block { result = val[1] }
;
# All the alternatives for ending an if-else block.
@@ -385,10 +407,9 @@ rule
# The full complement of if blocks, including postfix one-liner ifs and unlesses.
If:
IF Expression
Then Expressions IfEnd { result = IfNode.new(val[1], val[3], val[4]) }
| Expression IF Expression { result = IfNode.new(val[2], Expressions.new([val[0]]), nil, {:statement => true}) }
| Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.new([val[0]]), nil, {:statement => true, :invert => true}) }
IfBlock IfEnd { result = val[0].add_else(val[1]) }
| Expression IF Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true}) }
| Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true, :invert => true}) }
;
end

View File

@@ -12,69 +12,80 @@ module CoffeeScript
"new", "return",
"try", "catch", "finally", "throw",
"break", "continue",
"for", "in", "while",
"for", "in", "by", "where", "while",
"switch", "when",
"super", "extends",
"delete", "instanceof", "typeof"]
# Token matching regexes.
IDENTIFIER = /\A([a-zA-Z$_]\w*)/
NUMBER = /\A((\b|-)((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
NUMBER = /\A(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
STRING = /\A(""|''|"(.*?)[^\\]"|'(.*?)[^\\]')/m
JS = /\A(``|`(.*?)[^\\]`)/m
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
WHITESPACE = /\A([ \t\r]+)/
NEWLINE = /\A(\n+)/
COMMENT = /\A((#[^\n]*\s*)+)/m
WHITESPACE = /\A([ \t]+)/
COMMENT = /\A(((\n?[ \t]*)?#.*$)+)/
CODE = /\A(=>)/
REGEX = /\A(\/(.*?)[^\\]\/[imgy]{0,4})/
MULTI_DENT = /\A((\n([ \t]*)?)+)/
LAST_DENT = /\n([ \t]*)/
ASSIGNMENT = /\A(:|=)\Z/
# Token cleaning regexes.
JS_CLEANER = /(\A`|`\Z)/
MULTILINER = /\n/
COMMENT_CLEANER = /(^\s*#|\n\s*$)/
NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/
# Tokens that always constitute the start of an expression.
EXP_START = ['{', '(', '[']
# Tokens that always constitute the end of an expression.
EXP_END = ['}', ')', ']']
# Assignment tokens.
ASSIGN = [':', '=']
# Tokens which a regular expression will never immediately follow, but which
# a division operator might.
# See: http://www.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
NOT_REGEX = [
:IDENTIFIER, :NUMBER, :REGEX, :STRING,
')', '++', '--', ']', '}',
:FALSE, :NULL, :THIS, :TRUE
]
# Scan by attempting to match tokens one character at a time. Slow and steady.
def tokenize(code)
@code = code.chomp # Cleanup code by remove extra line breaks
@i = 0 # Current character position we're parsing
@line = 1 # The current line.
@indent = 0 # The current indent level.
@indents = [] # The stack of all indent levels we are currently within.
@tokens = [] # Collection of all parsed tokens in the form [:TOKEN_TYPE, value]
while @i < @code.length
@chunk = @code[@i..-1]
extract_next_token
end
@tokens
puts "original stream: #{@tokens.inspect}" if ENV['VERBOSE']
close_indentation
Rewriter.new.rewrite(@tokens)
end
# At every position, run this list of match attempts, short-circuiting if
# any of them succeed.
# At every position, run through this list of attempted matches,
# short-circuiting if any of them succeed.
def extract_next_token
return if identifier_token
return if number_token
return if string_token
return if js_token
return if regex_token
return if indent_token
return if comment_token
return if whitespace_token
return literal_token
end
# Tokenizers ==========================================================
# Matches identifying literals: variables, keywords, method names, etc.
def identifier_token
return false unless identifier = @chunk[IDENTIFIER, 1]
# Keywords are special identifiers tagged with their own name, 'if' will result
# in an [:IF, "if"] token
# Keywords are special identifiers tagged with their own name,
# 'if' will result in an [:IF, "if"] token.
tag = KEYWORDS.include?(identifier) ? identifier.upcase.to_sym : :IDENTIFIER
tag = :LEADING_WHEN if tag == :WHEN && [:OUTDENT, :INDENT, "\n"].include?(last_tag)
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2][1] == '.')
token(tag, identifier)
@i += identifier.length
@@ -108,6 +119,7 @@ module CoffeeScript
# Matches regular expression literals.
def regex_token
return false unless regex = @chunk[REGEX, 1]
return false if NOT_REGEX.include?(last_tag)
token(:REGEX, regex)
@i += regex.length
end
@@ -121,73 +133,105 @@ module CoffeeScript
@i += comment.length
end
# Record tokens for indentation differing from the previous line.
def indent_token
return false unless indent = @chunk[MULTI_DENT, 1]
@line += indent.scan(MULTILINER).size
@i += indent.size
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
token(:INDENT, size - @indent)
@indents << (size - @indent)
else
outdent_token(@indent - size)
end
@indent = size
end
# Record an oudent token or tokens, if we're moving back inwards past
# multiple recorded indents.
def outdent_token(move_out)
while move_out > 0 && !@indents.empty?
last_indent = @indents.pop
token(:OUTDENT, last_indent)
move_out -= last_indent
end
token("\n", "\n")
end
# Matches and consumes non-meaningful whitespace.
def whitespace_token
return false unless whitespace = @chunk[WHITESPACE, 1]
@i += whitespace.length
end
# Multiple newlines get merged together.
# Use a trailing \ to escape newlines.
def newline_token(newlines)
token("\n", "\n") unless last_value == "\n"
true
end
# Tokens to explicitly escape newlines are removed once their job is done.
def suppress_newlines(newlines)
@tokens.pop if last_value == "\\"
true
end
# We treat all other single characters as a token. Eg.: ( ) , . !
# Multi-character operators are also literal tokens, so that Racc can assign
# the proper order of operations. Multiple newlines get merged together.
# Use a trailing \ to escape newlines.
# the proper order of operations.
def literal_token
value = @chunk[NEWLINE, 1]
if value
@line += value.length
token("\n", "\n") unless ["\n", "\\"].include?(last_value)
@tokens.pop if last_value == "\\"
return @i += value.length
end
value = @chunk[OPERATOR, 1]
tag_parameters if value && value.match(CODE)
value ||= @chunk[0,1]
skip_following_newlines if EXP_START.include?(value)
remove_leading_newlines if EXP_END.include?(value)
tag = ASSIGN.include?(value) ? :ASSIGN : value
tag = value.match(ASSIGNMENT) ? :ASSIGN : value
token(tag, value)
@i += value.length
end
# Helpers ==========================================================
# Add a token to the results, taking note of the line number, and
# immediately-preceding comment.
def token(tag, value)
@tokens << [tag, Value.new(value, @line)]
end
# Peek at the previous token.
# Peek at the previous token's value.
def last_value
@tokens.last && @tokens.last[1]
end
# Peek at the previous token's tag.
def last_tag
@tokens.last && @tokens.last[0]
end
# A source of ambiguity in our grammar was parameter lists in function
# definitions (as opposed to argument lists in function calls). Tag
# parameter identifiers in order to avoid this.
# parameter identifiers in order to avoid this. Also, parameter lists can
# make use of splats.
def tag_parameters
index = 0
i = 0
loop do
tok = @tokens[index -= 1]
i -= 1
tok = @tokens[i]
return if !tok
next if tok[0] == ','
next tok[0] = :PARAM_SPLAT if tok[0] == '*'
return if tok[0] != :IDENTIFIER
tok[0] = :PARAM
end
end
# Consume and ignore newlines immediately after this point.
def skip_following_newlines
newlines = @code[(@i+1)..-1][NEWLINE, 1]
if newlines
@line += newlines.length
@i += newlines.length
end
end
# Discard newlines immediately before this point.
def remove_leading_newlines
@tokens.pop if last_value == "\n"
# Close up all remaining open blocks. IF the first token is an indent,
# axe it.
def close_indentation
outdent_token(@indent)
end
end
end

View File

@@ -15,15 +15,16 @@ coffeePath: File.path(module.path).dirname().dirname().dirname().dirname().dirna
checkForErrors: coffeeProcess =>
return true if coffeeProcess.wait() is 0
system.stderr.print(coffeeProcess.stderr.read())
throw new Error("CoffeeScript compile error").
throw new Error("CoffeeScript compile error")
# Run a simple REPL, round-tripping to the CoffeeScript compiler for every
# command.
exports.run: args =>
args.shift()
if args.length
exports.evalCS(File.read(path)) for path in args.
return true.
for path, i in args
exports.evalCS(File.read(path))
delete args[i]
return true
while true
try
@@ -31,31 +32,31 @@ exports.run: args =>
result: exports.evalCS(Readline.readline())
print(result) if result isnt undefined
catch e
print(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().
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().
coffee.stdout.read()
# Evaluating a string of CoffeeScript first compiles it externally.
exports.evalCS: source =>
eval(exports.compile(source)).
eval(exports.compile(source))
# Make a factory for the CoffeeScript environment.
exports.makeNarwhalFactory: path =>
code: exports.compileFile(path)
factoryText: "function(require,exports,module,system,print){ 1 + 1 /**/\n}"
if system.engine is "rhino"
Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null)
else
# eval requires parenthesis, but parenthesis break compileFunction.
eval("(" + factoryText + ")")..
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 + ")")

View File

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

View File

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

View File

@@ -1,14 +1,16 @@
(function(){
var File, OS, Readline, checkForErrors, coffeePath;
// This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee Executes the `coffee` Ruby program to convert from CoffeeScript
// to Javascript. Eventually this will hopefully happen entirely within JS. Require external dependencies.
// This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee
// Executes the `coffee` Ruby program to convert from CoffeeScript
// to Javascript. Eventually this will hopefully happen entirely within JS.
// Require external dependencies.
OS = require('os');
File = require('file');
Readline = require('readline');
// The path to the CoffeeScript Compiler.
coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee');
// Our general-purpose error handler.
checkForErrors = function(coffeeProcess) {
checkForErrors = function checkForErrors(coffeeProcess) {
if (coffeeProcess.wait() === 0) {
return true;
}
@@ -17,17 +19,20 @@
};
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every
// command.
exports.run = function(args) {
var __a, __b, __c, __d, path, result;
args.shift();
exports.run = function run(args) {
var __a, __b, __c, i, path, result;
if (args.length) {
__a = args;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
path = __a[__b];
__d[__b] = exports.evalCS(File.read(path));
__b = [];
for (i in __a) {
if (__a.hasOwnProperty(i)) {
path = __a[i];
exports.evalCS(File.read(path));
__c = delete args[i];
__b.push(__c);
}
}
__d;
__b;
return true;
}
while (true) {
@@ -43,14 +48,14 @@
}
};
// Compile a given CoffeeScript file into JavaScript.
exports.compileFile = function(path) {
exports.compileFile = function compileFile(path) {
var coffee;
coffee = OS.popen([coffeePath, "--print", "--no-wrap", path]);
checkForErrors(coffee);
return coffee.stdout.read();
};
// Compile a string of CoffeeScript into JavaScript.
exports.compile = function(source) {
exports.compile = function compile(source) {
var coffee;
coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
coffee.stdin.write(source).flush().close();
@@ -58,18 +63,18 @@
return coffee.stdout.read();
};
// Evaluating a string of CoffeeScript first compiles it externally.
exports.evalCS = function(source) {
exports.evalCS = function evalCS(source) {
return eval(exports.compile(source));
};
// Make a factory for the CoffeeScript environment.
exports.makeNarwhalFactory = function(path) {
exports.makeNarwhalFactory = function makeNarwhalFactory(path) {
var code, factoryText;
code = exports.compileFile(path);
factoryText = "function(require,exports,module,system,print){ 1 + 1 /**/\n}";
factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
if (system.engine === "rhino") {
return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
} else {
// eval requires parenthesis, but parenthesis break compileFunction.
// eval requires parentheses, but parentheses break compileFunction.
return eval("(" + factoryText + ")");
}
};

View File

@@ -6,12 +6,14 @@
};
loader = {
// Reload the coffee-script environment from source.
reload: function(topId, path) {
coffeescript = coffeescript || require('./coffee-script');
return (factories[topId] = coffeescript.makeNarwhalFactory(path));
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(topId, path) {
load: function load(topId, path) {
return factories[topId] = factories[topId] || this.reload(topId, path);
}
};

View File

@@ -7,12 +7,12 @@ loader: {
# Reload the coffee-script environment from source.
reload: topId, path =>
coffeescript ||= require('./coffee-script')
factories[topId]: coffeescript.makeNarwhalFactory(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).
factories[topId] ||= this.reload(topId, path)
}

View File

@@ -11,17 +11,11 @@ module CoffeeScript
class_eval "def statement?; true; end"
end
# Tag this node as having a custom return, meaning that instead of returning
# it from the outside, you ask it to return itself, and it obliges.
def self.custom_return
class_eval "def custom_return?; true; end"
end
# Tag this node as having a custom assignment, meaning that instead of
# assigning it to a variable name from the outside, you pass it the variable
# name and let it take care of it.
def self.custom_assign
class_eval "def custom_assign?; true; end"
# Tag this node as a statement that cannot be transformed into an expression.
# (break, continue, etc.) It doesn't make sense to try to transform it.
def self.statement_only
statement
class_eval "def statement_only?; true; end"
end
def write(code)
@@ -29,16 +23,27 @@ module CoffeeScript
code
end
# This is extremely important -- we convert JS statements into expressions
# by wrapping them in a closure, only if it's possible, and we're not at
# the top level of a block (which would be unnecessary), and we haven't
# already been asked to return the result.
def compile(o={})
@options = o.dup
top = self.is_a?(ForNode) ? @options[:top] : @options.delete(:top)
closure = statement? && !statement_only? && !top && !@options[:return]
closure ? compile_closure(@options) : compile_node(@options)
end
def compile_closure(o={})
indent = o[:indent]
o[:indent] += TAB
"(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
end
# Default implementations of the common node methods.
def unwrap; self; end
def line_ending; ';'; end
def statement?; false; end
def custom_return?; false; end
def custom_assign?; false; end
def unwrap; self; end
def statement?; false; end
def statement_only?; false; end
end
# A collection of nodes, each one representing an expression.
@@ -49,12 +54,13 @@ module CoffeeScript
STRIP_TRAILING_WHITESPACE = /\s+$/
# Wrap up a node as an Expressions, unless it already is.
def self.wrap(node)
node.is_a?(Expressions) ? node : Expressions.new([node])
def self.wrap(*nodes)
return nodes[0] if nodes.length == 1 && nodes[0].is_a?(Expressions)
Expressions.new(*nodes)
end
def initialize(nodes)
@expressions = nodes
def initialize(*nodes)
@expressions = nodes.flatten
end
# Tack an expression onto the end of this node.
@@ -63,6 +69,11 @@ module CoffeeScript
self
end
def unshift(node)
@expressions.unshift(node)
self
end
# If this Expressions consists of a single node, pull it back out.
def unwrap
@expressions.length == 1 ? @expressions.first : self
@@ -74,47 +85,48 @@ module CoffeeScript
node == @expressions[@last_index]
end
def compile(o={})
o[:scope] ? super(o) : compile_root(o)
end
# The extra fancy is to handle pushing down returns to the final lines of
# inner statements. Variables first defined within the Expressions body
# have their declarations pushed up top of the closest scope.
def compile_node(options={})
compiled = @expressions.map do |node|
o = options.dup
returns = o.delete(:return)
if last?(node) && returns && !node.statement_only?
if node.statement?
node.compile(o.merge(:return => true))
else
"#{o[:indent]}return #{node.compile(o)};"
end
else
ending = node.statement? ? '' : ';'
indent = node.statement? ? '' : o[:indent]
"#{indent}#{node.compile(o.merge(:top => true))}#{ending}"
end
end
write(compiled.join("\n"))
end
# If this is the top-level Expressions, wrap everything in a safety closure.
def root_compile(o={})
def compile_root(o={})
indent = o[:no_wrap] ? '' : TAB
code = compile(o.merge(:indent => indent, :scope => Scope.new), o[:no_wrap] ? nil : :code)
o.merge!(:indent => indent, :scope => Scope.new(nil, self))
code = o[:no_wrap] ? compile_node(o) : compile_with_declarations(o)
code.gsub!(STRIP_TRAILING_WHITESPACE, '')
o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
end
# The extra fancy is to handle pushing down returns and assignments
# recursively to the final lines of inner statements.
# Variables first defined within the Expressions body have their
# declarations pushed up to the top scope.
def compile(options={}, parent=nil)
return root_compile(options) unless options[:scope]
compiled = @expressions.map do |node|
o = super(options)
if last?(node) && (o[:return] || o[:assign])
if o[:return]
if node.statement? || node.custom_return?
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
else
o.delete(:return)
"#{o[:indent]}return #{node.compile(o)}#{node.line_ending}"
end
elsif o[:assign]
if node.statement? || node.custom_assign?
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
else
"#{o[:indent]}#{AssignNode.new(o[:assign], node).compile(o)};"
end
end
else
o.delete(:return) and o.delete(:assign)
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
end
end
scope = options[:scope]
declarations = scope.any_declared? && parent == :code ? "#{options[:indent]}var #{scope.declared_variables.join(', ')};\n" : ''
code = declarations + compiled.join("\n")
write(code)
def compile_with_declarations(o={})
code = compile_node(o)
decls = ''
decls = "#{o[:indent]}var #{o[:scope].declared_variables.join(', ')};\n" if o[:scope].declarations?(self)
decls + code
end
end
# Literals are static values that have a Ruby representation, eg.: a string, a number,
@@ -131,21 +143,18 @@ module CoffeeScript
def statement?
STATEMENTS.include?(@value.to_s)
end
alias_method :statement_only?, :statement?
def line_ending
@value.to_s[-1..-1] == ';' ? '' : ';'
end
def compile(o={})
o = super(o)
write(@value.to_s)
def compile_node(o)
indent = statement? ? o[:indent] : ''
ending = statement? ? ';' : ''
write(indent + @value.to_s + ending)
end
end
# Try to return your expression, or tell it to return itself.
class ReturnNode < Node
statement
custom_return
statement_only
attr_reader :expression
@@ -153,32 +162,23 @@ module CoffeeScript
@expression = expression
end
def line_ending
@expression.custom_return? ? '' : ';'
end
def compile(o={})
o = super(o)
return write(@expression.compile(o.merge(:return => true))) if @expression.custom_return?
def compile_node(o)
return write(@expression.compile(o.merge(:return => true))) if @expression.statement?
compiled = @expression.compile(o)
write(@expression.statement? ? "#{compiled}\n#{indent}return null" : "return #{compiled}")
write(@expression.statement? ? "#{compiled}\n#{o[:indent]}return null;" : "#{o[:indent]}return #{compiled};")
end
end
# Pass through CoffeeScript comments into JavaScript comments at the
# same position.
class CommentNode < Node
statement
statement_only
def initialize(lines)
@lines = lines.value
end
def line_ending
''
end
def compile(o={})
def compile_node(o={})
delimiter = "\n#{o[:indent]}//"
comment = "#{delimiter}#{@lines.join(delimiter)}"
write(comment)
@@ -189,8 +189,6 @@ module CoffeeScript
# Node for a function invocation. Takes care of converting super() calls into
# calls against the prototype's function of the same name.
class CallNode < Node
LEADING_DOT = /\A\./
attr_reader :variable, :arguments
def initialize(variable, arguments=[])
@@ -206,42 +204,65 @@ module CoffeeScript
@variable == :super
end
def compile(o={})
o = super(o)
def prefix
@new ? "new " : ''
end
def splat?
@arguments.any? {|a| a.is_a?(ArgSplatNode) }
end
def <<(argument)
@arguments << argument
end
def compile_node(o)
return write(compile_splat(o)) if splat?
args = @arguments.map{|a| a.compile(o) }.join(', ')
return write(compile_super(args, o)) if super?
prefix = @new ? "new " : ''
write("#{prefix}#{@variable.compile(o)}(#{args})")
end
def compile_super(args, o)
methname = o[:last_assign].sub(LEADING_DOT, '')
methname = o[:last_assign]
arg_part = args.empty? ? '' : ", #{args}"
"#{o[:proto_assign]}.__superClass__.#{methname}.call(this#{arg_part})"
end
def compile_splat(o)
meth = @variable.compile(o)
obj = @variable.source || 'this'
args = @arguments.map do |arg|
code = arg.compile(o)
code = arg.is_a?(ArgSplatNode) ? code : "[#{code}]"
arg.equal?(@arguments.first) ? code : ".concat(#{code})"
end
"#{prefix}#{meth}.apply(#{obj}, #{args.join('')})"
end
end
# Node to extend an object's prototype with an ancestor object.
# After goog.inherits from the Closure Library.
class ExtendsNode < Node
statement
attr_reader :sub_object, :super_object
def initialize(sub_object, super_object)
@sub_object, @super_object = sub_object, super_object
end
def compile(o={})
def compile_node(o={})
sub, sup = @sub_object.compile(o), @super_object.compile(o)
"#{sub}.__superClass__ = #{sup}.prototype;\n#{o[:indent]}" +
"#{o[:indent]}#{sub}.__superClass__ = #{sup}.prototype;\n#{o[:indent]}" +
"#{sub}.prototype = new #{sup}();\n#{o[:indent]}" +
"#{sub}.prototype.constructor = #{sub}"
"#{sub}.prototype.constructor = #{sub};"
end
end
# A value, indexed or dotted into, or vanilla.
class ValueNode < Node
attr_reader :literal, :properties, :last
attr_reader :literal, :properties, :last, :source
def initialize(literal, properties=[])
@literal, @properties = literal, properties
@@ -260,20 +281,14 @@ module CoffeeScript
@literal.is_a?(Node) && @literal.statement? && !properties?
end
def custom_assign?
@literal.is_a?(Node) && @literal.custom_assign? && !properties?
end
def custom_return?
@literal.is_a?(Node) && @literal.custom_return? && !properties?
end
def compile(o={})
o = super(o)
parts = [@literal, @properties].flatten.map do |val|
def compile_node(o)
only = o.delete(:only_first)
props = only ? @properties[0...-1] : @properties
parts = [@literal, props].flatten.map do |val|
val.respond_to?(:compile) ? val.compile(o) : val.to_s
end
@last = parts.last
@source = parts.length > 1 ? parts[0...-1].join('') : nil
write(parts.join(''))
end
end
@@ -286,8 +301,7 @@ module CoffeeScript
@name = name
end
def compile(o={})
o = super(o)
def compile_node(o)
write(".#{@name}")
end
end
@@ -300,15 +314,14 @@ module CoffeeScript
@index = index
end
def compile(o={})
o = super(o)
def compile_node(o)
write("[#{@index.compile(o)}]")
end
end
# A range literal. Ranges can be used to extract portions (slices) of arrays,
# or to specify a range for array comprehensions.
class RangeNode
class RangeNode < Node
attr_reader :from, :to
def initialize(from, to, exclusive=false)
@@ -327,12 +340,21 @@ module CoffeeScript
@exclusive ? '>' : '>='
end
def compile(o, fv, tv)
fvv, tvv = @from.compile(o), @to.compile(o)
vars = "#{fv}=#{fvv}, #{tv}=#{tvv}"
compare = "(#{fvv} <= #{tvv} ? #{fv} #{less_operator} #{tv} : #{fv} #{greater_operator} #{tv})"
incr = "(#{fvv} <= #{tvv} ? #{fv} += 1 : #{fv} -= 1)"
"#{vars}; #{compare}; #{incr}"
def compile_variables(o)
idt = o[:indent]
@from_var, @to_var = o[:scope].free_variable, o[:scope].free_variable
from_val, to_val = @from.compile(o), @to.compile(o)
write("#{idt}#{@from_var} = #{from_val};\n#{idt}#{@to_var} = #{to_val};\n#{idt}")
end
def compile_node(o)
idx, step = o.delete(:index), o.delete(:step)
raise SyntaxError, "unexpected range literal" unless idx
vars = "#{idx}=#{@from_var}"
step = step ? step.compile(o) : '1'
compare = "(#{@from_var} <= #{@to_var} ? #{idx} #{less_operator} #{@to_var} : #{idx} #{greater_operator} #{@to_var})"
incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})"
write("#{vars}; #{compare}; #{incr}")
end
end
@@ -347,8 +369,7 @@ module CoffeeScript
@range = range
end
def compile(o={})
o = super(o)
def compile_node(o)
from = @range.from.compile(o)
to = @range.to.compile(o)
plus_part = @range.exclusive? ? '' : ' + 1'
@@ -359,8 +380,7 @@ module CoffeeScript
# Setting the value of a local variable, or the value of an object property.
class AssignNode < Node
PROTO_ASSIGN = /\A(\S+)\.prototype/
custom_return
LEADING_DOT = /\A\./
attr_reader :variable, :value, :context
@@ -368,21 +388,26 @@ module CoffeeScript
@variable, @value, @context = variable, value, context
end
def line_ending
@value.custom_assign? ? '' : ';'
end
def compile(o={})
o = super(o)
def compile_node(o)
return compile_splice(o) if @variable.properties.last.is_a?(SliceNode)
name = @variable.compile(o)
last = @variable.last.to_s
last = @variable.last.to_s.sub(LEADING_DOT, '')
proto = name[PROTO_ASSIGN, 1]
o = o.merge(:assign => @variable, :last_assign => last, :proto_assign => proto)
o = o.merge(:last_assign => last, :proto_assign => proto)
o[:immediate_assign] = last if @value.is_a?(CodeNode) && last.match(Lexer::IDENTIFIER)
return write("#{name}: #{@value.compile(o)}") if @context == :object
o[:scope].find(name) unless @variable.properties?
return write(@value.compile(o)) if @value.custom_assign?
val = "#{name} = #{@value.compile(o)}"
write(o[:return] && !@value.custom_return? ? "return (#{val})" : val)
write(o[:return] ? "#{o[:indent]}return (#{val})" : val)
end
def compile_splice(o)
var = @variable.compile(o.merge(:only_first => true))
range = @variable.properties.last.range
plus = range.exclusive? ? '' : ' + 1'
from = range.from.compile(o)
to = "#{range.to.compile(o)} - #{from}#{plus}"
write("#{var}.splice.apply(#{var}, [#{from}, #{to}].concat(#{@value.compile(o)}))")
end
end
@@ -390,31 +415,30 @@ module CoffeeScript
# CoffeeScript operations into their JavaScript equivalents.
class OpNode < Node
CONVERSIONS = {
"==" => "===",
"!=" => "!==",
'and' => '&&',
'or' => '||',
'is' => '===',
"isnt" => "!==",
'not' => '!'
:== => "===",
:'!=' => "!==",
:and => '&&',
:or => '||',
:is => '===',
:isnt => "!==",
:not => '!'
}
CONDITIONALS = ['||=', '&&=']
PREFIX_OPERATORS = ['typeof', 'delete']
CONDITIONALS = [:'||=', :'&&=']
PREFIX_OPERATORS = [:typeof, :delete]
attr_reader :operator, :first, :second
def initialize(operator, first, second=nil, flip=false)
@first, @second, @flip = first, second, flip
@operator = CONVERSIONS[operator] || operator
@operator = CONVERSIONS[operator.to_sym] || operator
end
def unary?
@second.nil?
end
def compile(o={})
o = super(o)
return write(compile_conditional(o)) if CONDITIONALS.include?(@operator)
def compile_node(o)
return write(compile_conditional(o)) if CONDITIONALS.include?(@operator.to_sym)
return write(compile_unary(o)) if unary?
write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}")
end
@@ -426,7 +450,7 @@ module CoffeeScript
end
def compile_unary(o)
space = PREFIX_OPERATORS.include?(@operator.to_s) ? ' ' : ''
space = PREFIX_OPERATORS.include?(@operator.to_sym) ? ' ' : ''
parts = [@operator.to_s, space, @first.compile(o)]
parts.reverse! if @flip
parts.join('')
@@ -442,20 +466,55 @@ module CoffeeScript
@body = body
end
def compile(o={})
o = super(o)
o[:scope] = Scope.new(o[:scope])
o[:return] = true
indent = o[:indent]
o[:indent] += TAB
o.delete(:assign)
def compile_node(o)
shared_scope = o.delete(:shared_scope)
indent = o[:indent]
o[:scope] = shared_scope || Scope.new(o[:scope], @body)
o[:return] = true
o[:top] = true
o[:indent] += TAB
o.delete(:no_wrap)
name = o.delete(:immediate_assign)
if @params.last.is_a?(ParamSplatNode)
splat = @params.pop
splat.index = @params.length
@body.unshift(splat)
end
@params.each {|id| o[:scope].parameter(id.to_s) }
code = @body.compile(o, :code)
write("function(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
code = @body.compile_with_declarations(o)
name_part = name ? " #{name}" : ''
write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
end
end
# A parameter splat in a function definition.
class ParamSplatNode < Node
attr_accessor :index
attr_reader :name
def initialize(name)
@name = name
end
def compile_node(o={})
o[:scope].find(@name)
write("#{@name} = Array.prototype.slice.call(arguments, #{@index})")
end
end
class ArgSplatNode < Node
attr_reader :value
def initialize(value)
@value = value
end
def compile_node(o={})
write(@value.compile(o))
end
end
# An object literal.
class ObjectNode < Node
attr_reader :properties
@@ -464,13 +523,20 @@ module CoffeeScript
@properties = properties
end
def compile(o={})
o = super(o)
# All the mucking about with commas is to make sure that CommentNodes and
# AssignNodes get interleaved correctly, with no trailing commas or
# commas affixed to comments. TODO: Extract this and add it to ArrayNode.
def compile_node(o)
indent = o[:indent]
o[:indent] += TAB
props = @properties.map { |prop|
joiner = prop == @properties.last ? '' : prop.is_a?(CommentNode) ? "\n" : ",\n"
o[:indent] + prop.compile(o) + joiner
joins = Hash.new("\n")
non_comments = @properties.select {|p| !p.is_a?(CommentNode) }
non_comments.each {|p| joins[p] = p == non_comments.last ? "\n" : ",\n" }
props = @properties.map { |prop|
join = joins[prop]
join = '' if prop == @properties.last
idt = prop.is_a?(CommentNode) ? '' : o[:indent]
"#{idt}#{prop.compile(o)}#{join}"
}.join('')
write("{\n#{props}\n#{indent}}")
end
@@ -484,13 +550,15 @@ module CoffeeScript
@objects = objects
end
def compile(o={})
o = super(o)
def compile_node(o)
indent = o[:indent]
o[:indent] += TAB
objects = @objects.map { |obj|
joiner = obj.is_a?(CommentNode) ? "\n#{o[:indent] + TAB}" : obj == @objects.last ? '' : ', '
obj.compile(o.merge(:indent => o[:indent] + TAB)) + joiner
code = obj.compile(o)
obj.is_a?(CommentNode) ? "\n#{code}\n#{o[:indent]}" :
obj == @objects.last ? code : "#{code}, "
}.join('')
ending = objects.include?("\n") ? "\n#{o[:indent]}]" : ']'
ending = objects.include?("\n") ? "\n#{indent}]" : ']'
write("[#{objects}#{ending}")
end
end
@@ -506,16 +574,14 @@ module CoffeeScript
@condition, @body = condition, body
end
def line_ending
''
end
def compile(o={})
o = super(o)
o.delete(:return)
indent = o[:indent] + TAB
cond = @condition.compile(o)
write("while (#{cond}) {\n#{@body.compile(o.merge(:indent => indent))}\n#{o[:indent]}}")
def compile_node(o)
returns = o.delete(:return)
indent = o[:indent]
o[:indent] += TAB
o[:top] = true
cond = @condition.compile(o)
post = returns ? "\n#{indent}return null;" : ''
write("#{indent}while (#{cond}) {\n#{@body.compile(o)}\n#{indent}}#{post}")
end
end
@@ -525,74 +591,70 @@ module CoffeeScript
# the current index of the loop as a second parameter.
class ForNode < Node
statement
custom_return
custom_assign
attr_reader :body, :source, :name, :filter, :index
attr_reader :body, :source, :name, :index, :filter, :step
def initialize(body, source, name, filter, index=nil)
@body, @source, @name, @filter, @index = body, source, name, filter, index
def initialize(body, source, name, index=nil)
@body, @name, @index = body, name, index
@source = source[:source]
@filter = source[:filter]
@step = source[:step]
end
def line_ending
''
end
def compile(o={})
o = super(o)
def compile_node(o)
top_level = o.delete(:top) && !o[:return]
range = @source.is_a?(RangeNode)
scope = o[:scope]
name_found = scope.find(@name)
index_found = @index && scope.find(@index)
svar = scope.free_variable
ivar = range ? name : scope.free_variable
lvar = scope.free_variable
rvar = scope.free_variable
index_name = @index ? @index : nil
ivar = range ? name : @index ? @index : scope.free_variable
rvar = scope.free_variable unless top_level
tvar = scope.free_variable
if range
source_part = ''
var_part = ''
index_part = ''
body_dent = o[:indent] + TAB
var_part, pre_cond, post_cond = '', '', ''
index_var = scope.free_variable
for_part = "#{index_var}=0, #{@source.compile(o, ivar, lvar)}, #{index_var}++"
source_part = @source.compile_variables(o)
for_part = "#{index_var}=0, #{@source.compile(o.merge(:index => ivar, :step => @step))}, #{index_var}++"
else
index_var = nil
source_part = "#{svar} = #{@source.compile(o)};\n#{o[:indent]}"
for_part = "#{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
var_part = "\n#{o[:indent] + TAB}#{@name} = #{svar}[#{ivar}];"
index_part = @index ? "\n#{o[:indent] + TAB}#{index_name} = #{ivar};" : ''
body_dent = o[:indent] + TAB + TAB
source_part = "#{o[:indent]}#{svar} = #{@source.compile(o)};\n#{o[:indent]}"
for_part = "#{ivar} in #{svar}"
pre_cond = "\n#{o[:indent] + TAB}if (#{svar}.hasOwnProperty(#{ivar})) {"
var_part = "\n#{body_dent}#{@name} = #{svar}[#{ivar}];"
post_cond = "\n#{o[:indent] + TAB}}"
end
body = @body
suffix = ';'
set_result = "#{rvar} = [];\n#{o[:indent]}"
save_result = "#{rvar}[#{index_var || ivar}] = "
return_result = rvar
if o[:return] || o[:assign]
return_result = "#{o[:assign].compile(o)} = #{return_result}" if o[:assign]
set_result = rvar ? "#{rvar} = [];\n#{o[:indent]}" : ''
return_result = rvar || ''
temp_var = ValueNode.new(LiteralNode.new(tvar))
if top_level
body = Expressions.wrap(body)
else
body = Expressions.wrap(
AssignNode.new(temp_var, @body.unwrap),
CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [temp_var])
)
end
if o[:return]
return_result = "return #{return_result}" if o[:return]
if @filter
body = CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [@body])
body = IfNode.new(@filter, body, nil, :statement => true)
save_result = ''
suffix = ''
end
o.delete(:return)
body = IfNode.new(@filter, body, nil, :statement => true) if @filter
elsif @filter
body = IfNode.new(@filter, @body)
body = Expressions.wrap(IfNode.new(@filter, @body))
end
return_result = "\n#{o[:indent]}#{return_result};"
indent = o[:indent] + TAB
body = body.compile(o.merge(:indent => indent))
write("#{source_part}#{set_result}for (#{for_part}) {#{var_part}#{index_part}\n#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}")
return_result = "\n#{o[:indent]}#{return_result};" unless top_level
body = body.compile(o.merge(:indent => body_dent, :top => true))
write("#{source_part}#{set_result}for (#{for_part}) {#{pre_cond}#{var_part}\n#{body}#{post_cond}\n#{o[:indent]}}#{return_result}")
end
end
# A try/catch/finally block.
class TryNode < Node
statement
custom_return
custom_assign
attr_reader :try, :error, :recovery, :finally
@@ -600,24 +662,20 @@ module CoffeeScript
@try, @error, @recovery, @finally = try, error, recovery, finally
end
def line_ending
''
end
def compile(o={})
o = super(o)
def compile_node(o)
indent = o[:indent]
o[:indent] += TAB
o[:top] = true
error_part = @error ? " (#{@error}) " : ' '
catch_part = @recovery && " catch#{error_part}{\n#{@recovery.compile(o)}\n#{indent}}"
finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:assign => nil, :return => nil))}\n#{indent}}"
write("try {\n#{@try.compile(o)}\n#{indent}}#{catch_part}#{finally_part}")
finally_part = @finally && " finally {\n#{@finally.compile(o.merge(:return => nil))}\n#{indent}}"
write("#{indent}try {\n#{@try.compile(o)}\n#{indent}}#{catch_part}#{finally_part}")
end
end
# Throw an exception.
class ThrowNode < Node
statement
statement_only
attr_reader :expression
@@ -625,9 +683,22 @@ module CoffeeScript
@expression = expression
end
def compile(o={})
o = super(o)
write("throw #{@expression.compile(o)}")
def compile_node(o)
write("#{o[:indent]}throw #{@expression.compile(o)};")
end
end
# Check an expression for existence (meaning not null or undefined).
class ExistenceNode < Node
attr_reader :expression
def initialize(expression)
@expression = expression
end
def compile_node(o)
val = @expression.compile(o)
write("(typeof #{val} !== \"undefined\" && #{val} !== null)")
end
end
@@ -637,25 +708,12 @@ module CoffeeScript
class ParentheticalNode < Node
attr_reader :expressions
def initialize(expressions)
def initialize(expressions, line=nil)
@expressions = expressions.unwrap
@line = line
end
def statement?
@expressions.unwrap.statement?
end
def custom_assign?
@expressions.custom_assign?
end
def custom_return?
@expressions.custom_return?
end
def compile(o={})
raise SyntaxError, "parentheses can't be wrapped around a statement" if statement?
o = super(o)
def compile_node(o)
compiled = @expressions.compile(o)
compiled = compiled[0...-1] if compiled[-1..-1] == ';'
write("(#{compiled})")
@@ -683,6 +741,11 @@ module CoffeeScript
self
end
def force_statement
@tags[:statement] = true
self
end
# Rewrite a chain of IfNodes with their switch condition for equality.
def rewrite_condition(expression)
@condition = OpNode.new("is", expression, @condition)
@@ -691,8 +754,8 @@ module CoffeeScript
end
# Rewrite a chain of IfNodes to add a default case as the final else.
def add_else(expressions)
chain? ? @else_body.add_else(expressions) : @else_body = expressions
def add_else(exprs)
chain? ? @else_body.add_else(exprs) : @else_body = (exprs && exprs.unwrap)
self
end
@@ -707,20 +770,7 @@ module CoffeeScript
@is_statement ||= !!(@tags[:statement] || @body.statement? || (@else_body && @else_body.statement?))
end
def custom_return?
statement?
end
def custom_assign?
statement?
end
def line_ending
statement? ? '' : ';'
end
def compile(o={})
o = super(o)
def compile_node(o)
write(statement? ? compile_statement(o) : compile_ternary(o))
end
@@ -728,14 +778,16 @@ module CoffeeScript
# force sub-else bodies into statement form.
def compile_statement(o)
indent = o[:indent]
child = o.delete(:chain_child)
cond_o = o.dup
cond_o.delete(:assign)
cond_o.delete(:return)
o[:indent] += TAB
if_part = "if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
o[:top] = true
if_dent = child ? '' : indent
if_part = "#{if_dent}if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
return if_part unless @else_body
else_part = chain? ?
" else #{@else_body.compile(o.merge(:indent => indent))}" :
" else #{@else_body.compile(o.merge(:indent => indent, :chain_child => true))}" :
" else {\n#{Expressions.wrap(@else_body).compile(o)}\n#{indent}}"
if_part + else_part
end

View File

@@ -12,8 +12,9 @@ module CoffeeScript
def message
line = @value.respond_to?(:line) ? @value.line : "END"
line_part = "line #{line}:"
id_part = @token_id != @value.inspect ? ", unexpected #{@token_id.downcase}" : ""
"#{line_part} syntax error for '#{@value.to_s}'#{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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,208 @@
module CoffeeScript
# 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.
class Rewriter
# Tokens that must be balanced.
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], [:INDENT, :OUTDENT]]
# Tokens that signal the start of a balanced pair.
EXPRESSION_START = BALANCED_PAIRS.map {|pair| pair.first }
# Tokens that signal the end of a balanced pair.
EXPRESSION_TAIL = BALANCED_PAIRS.map {|pair| pair.last }
# Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE = [:CATCH, :WHEN, :ELSE, :FINALLY] + EXPRESSION_TAIL
# The inverse mappings of token pairs we're trying to fix up.
INVERSES = BALANCED_PAIRS.inject({}) do |memo, pair|
memo[pair.first] = pair.last
memo[pair.last] = pair.first
memo
end
# 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]
# 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.
def rewrite(tokens)
@tokens = tokens
adjust_comments
remove_mid_expression_newlines
move_commas_outside_outdents
add_implicit_indentation
ensure_balance(*BALANCED_PAIRS)
rewrite_closing_parens
@tokens
end
# 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.
def scan_tokens
i = 0
loop do
break unless @tokens[i]
move = yield(@tokens[i - 1], @tokens[i], @tokens[i + 1], i)
i += move
end
end
# Massage newlines and indentations so that comments don't have to be
# correctly indented, or appear on their own line.
def adjust_comments
scan_tokens do |prev, token, post, i|
next 1 unless token[0] == :COMMENT
before, after = @tokens[i - 2], @tokens[i + 2]
if before && after &&
((before[0] == :INDENT && after[0] == :OUTDENT) ||
(before[0] == :OUTDENT && after[0] == :INDENT)) &&
before[1] == after[1]
@tokens.delete_at(i + 2)
@tokens.delete_at(i - 2)
next 0
elsif !["\n", :INDENT, :OUTDENT].include?(prev[0])
@tokens.insert(i, ["\n", Value.new("\n", token[1].line)])
next 2
else
next 1
end
end
end
# Some blocks occur in the middle of expressions -- when we're expecting
# this, remove their trailing newlines.
def remove_mid_expression_newlines
scan_tokens do |prev, token, post, i|
next 1 unless post && EXPRESSION_CLOSE.include?(post[0]) && token[0] == "\n"
@tokens.delete_at(i)
next 0
end
end
# Make sure that we don't accidentally break trailing commas, which need
# to go on the outside of expression closers.
def move_commas_outside_outdents
scan_tokens do |prev, token, post, i|
if token[0] == :OUTDENT && prev[0] == ','
@tokens.delete_at(i)
@tokens.insert(i - 1, token)
end
next 1
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.
# ')' can close a single-line block, but we need to make sure it's balanced.
def add_implicit_indentation
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.
line = token[1].line
@tokens.insert(i + 1, [:INDENT, Value.new(2, line)])
idx = i + 1
parens = 0
loop do
idx += 1
tok = @tokens[idx]
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] == '('
parens -= 1 if tok[0] == ')'
end
next 1 unless token[0] == :THEN
@tokens.delete_at(i)
next 0
end
end
# Ensure that all listed pairs of tokens are correctly balanced throughout
# the course of the token stream.
def ensure_balance(*pairs)
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
raise ParseError.new(token[0], token[1], nil) if levels[open] < 0
end
next 1
end
unclosed = levels.detect {|k, v| v > 0 }
raise SyntaxError, "unclosed '#{unclosed[0]}'" if unclosed
end
# 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.
#
def rewrite_closing_parens
verbose = ENV['VERBOSE']
stack, debt = [], Hash.new(0)
stack_stats = lambda { "stack: #{stack.inspect} debt: #{debt.inspect}\n\n" }
puts "rewrite_closing_original: #{@tokens.inspect}" if verbose
scan_tokens do |prev, token, post, i|
tag, inv = token[0], INVERSES[token[0]]
# Push openers onto the stack.
if EXPRESSION_START.include?(tag)
stack.push(token)
puts "pushing #{tag} #{stack_stats[]}" if verbose
next 1
# The end of an expression, check stack and debt for a pair.
elsif EXPRESSION_TAIL.include?(tag)
puts @tokens[i..-1].inspect if verbose
# If the tag is already in our debt, swallow it.
if debt[inv] > 0
debt[inv] -= 1
@tokens.delete_at(i)
puts "tag in debt #{tag} #{stack_stats[]}" if verbose
next 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]
puts "expected tag #{tag} #{stack_stats[]}" if verbose
next 1
else
# Unexpected close, insert correct close, adding to the debt.
debt[mtag] += 1
puts "unexpected #{tag}, replacing with #{INVERSES[mtag]} #{stack_stats[]}" if verbose
val = mtag == :INDENT ? match[1] : INVERSES[mtag]
@tokens.insert(i, [INVERSES[mtag], Value.new(val, token[1].line)])
next 1
end
end
else
# Uninteresting token:
next 1
end
end
end
end
end

View File

@@ -5,18 +5,19 @@ module CoffeeScript
# whether a variable has been seen before or if it needs to be declared.
class Scope
attr_reader :parent, :variables, :temp_variable
attr_reader :parent, :expressions, :variables, :temp_variable
# Initialize a scope with its parent, for lookups up the chain.
def initialize(parent=nil)
@parent = parent
# Initialize a scope with its parent, for lookups up the chain,
# as well as the Expressions body where it should declare its variables.
def initialize(parent, expressions)
@parent, @expressions = parent, expressions
@variables = {}
@temp_variable = @parent ? @parent.temp_variable : '__a'
@temp_variable = @parent ? @parent.temp_variable.dup : '__a'
end
# Look up a variable in lexical scope, or declare it if not found.
def find(name, remote=false)
found = check(name, remote)
found = check(name)
return found if found || remote
@variables[name.to_sym] = :var
found
@@ -29,9 +30,9 @@ module CoffeeScript
end
# Just check to see if a variable has already been declared.
def check(name, remote=false)
def check(name)
return true if @variables[name.to_sym]
@parent && @parent.find(name, true)
!!(@parent && @parent.check(name))
end
# You can reset a found variable on the immediate scope.
@@ -46,8 +47,8 @@ module CoffeeScript
@temp_variable.dup
end
def any_declared?
!declared_variables.empty?
def declarations?(body)
!declared_variables.empty? && body == @expressions
end
# Return the list of variables first declared in current scope.
@@ -55,6 +56,10 @@ module CoffeeScript
@variables.select {|k, v| v == :var }.map {|pair| pair[0].to_s }.sort
end
def inspect
"<Scope:#{__id__} #{@variables.inspect}>"
end
end
end

View File

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

View File

@@ -0,0 +1,17 @@
area: x, y, x1, y1 =>
(x - x1) * (x - y1)
x: y: 10
x1: y1: 20
print(area(x, y, x1, y1) is 100)
print(area(x, y,
x1, y1) is 100)
print(area(
x
y
x1
y1
) is 100)

View File

@@ -1,4 +1,21 @@
nums: n * n for n in [1, 2, 3] if n % 2 isnt 0.
result: n * 2 for n in nums.
nums: n * n for n in [1, 2, 3] when n % 2 isnt 0
results: n * 2 for n in nums
print(results.join(',') is '2,18')
obj: {one: 1, two: 2, three: 3}
names: key + '!' for value, key in obj
odds: key + '!' for value, key in obj when value % 2 isnt 0
print(names.join(' ') is "one! two! three!")
print(odds.join(' ') is "one! three!")
evens: for num in [1, 2, 3, 4, 5, 6] when num % 2 is 0
num *= -1
num -= 2
num * -1
print(evens.join(', ') is '4, 6, 8')
print(result.join(',') is '2,18')

View File

@@ -1,6 +1,8 @@
result: try
nonexistent * missing
catch error
true.
print(result)
true
result2: try nonexistent * missing catch error then true
print(result is true and result2 is true)

View File

@@ -0,0 +1,4 @@
results: [1, 2, 3].map() x =>
x * x
print(results.join(' ') is '1 4 9')

View File

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

View File

@@ -1,4 +1,4 @@
identity_wrap: x => => x..
identity_wrap: x => => x
result: identity_wrap(identity_wrap(true))()()

View File

@@ -1,12 +1,15 @@
func: =>
a: 3
b: []
while a >= 0
b.push('o')
a--.
a--
c: {
"text": b
other: null
something_else: x => x + 5
}
c: 'error' unless 42 > 41
@@ -14,14 +17,13 @@ func: =>
c.text: if false
'error'
else
c.text + '---'.
c.text + '---'
d = {
text = c.text
}
c.list: l for l in d.text.split('') if l is '-'.
c.list: l for l in d.text.split('') when l is '-'
c.single: c.list[1..1][0].
c.single: c.list[1..1][0]
print(func() == '-')

View File

@@ -0,0 +1,5 @@
print(if my_special_variable? then false else true)
my_special_variable: false
print(if my_special_variable? then true else false)

View File

@@ -3,9 +3,8 @@ c: false
result: if a
if b
if c then false
else
if c then false else
if d
true....
true
print(result)

View File

@@ -0,0 +1,8 @@
func: =>
false
false # comment
false
# comment
true
print(func())

View File

@@ -0,0 +1,14 @@
a: [(x => x), (x => x * x)]
print(a.length is 2)
regex: /match/i
words: "I think there is a match in here."
print(!!words.match(regex))
neg: (3 -4)
print(neg is -1)

View File

@@ -0,0 +1,8 @@
x: 1
y: {}
y.x: => 3
print(x is 1)
print(typeof(y.x) is 'function')
print(y.x() is 3)
print(y.x.name is 'x')

View File

@@ -0,0 +1,11 @@
multi_liner:
for x in [3..5]
for y in [3..5]
[x, y]
single_liner:
[x, y] for y in [3..5] for x in [3..5]
print(multi_liner.length is single_liner.length)
print(5 is multi_liner[2][2][1])
print(5 is single_liner[2][2][1])

View File

@@ -1,6 +1,6 @@
six: \
1 + \
2 + \
3
six:
1 +
2 +
3
print(six is 6)

View File

@@ -1,8 +1,20 @@
nums: i * 3 for i in [1..3].
nums: i * 3 for i in [1..3]
negs: x for x in [-20..-10].
negs: x for x in [-20..-5*2]
negs: negs[0..2]
result: nums.concat(negs).join(', ')
print(result is '3, 6, 9, -20, -19, -18')
print(result is '3, 6, 9, -20, -19, -18')
# Ensure that ranges are safe. This used to infinite loop:
j = 5
result: for j in [j..(j+3)]
j
print(result.join(' ') is '5 6 7 8')
# With range comprehensions, you can loop in steps.
results: x for x in [0..25] by 5
print(results.join(' ') is '0 5 10 15 20 25')

View File

@@ -0,0 +1,35 @@
func: first, second, *rest =>
rest.join(' ')
result: func(1, 2, 3, 4, 5)
print(result is "3 4 5")
gold: silver: bronze: the_field: null
medalists: first, second, third, *rest =>
gold: first
silver: second
bronze: third
the_field: rest
contenders: [
"Michael Phelps"
"Liu Xiang"
"Yao Ming"
"Allyson Felix"
"Shawn Johnson"
"Roman Sebrle"
"Guo Jingjing"
"Tyson Gay"
"Asafa Powell"
"Usain Bolt"
]
medalists("Mighty Mouse", *contenders)
print(gold is "Mighty Mouse")
print(silver is "Michael Phelps")
print(bronze is "Liu Xiang")
print(the_field.length is 8)

View File

@@ -0,0 +1,5 @@
array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
array[5..10]: [0, 0, 0]
print(array.join(' ') is '0 1 2 3 4 0 0 0')

View File

@@ -1,11 +1,13 @@
num: 10
result: switch num
when 5 then false
when 'a'
false
when 10 then true
when 11 then false
else false.
when 5 then false
when 'a'
true
true
false
when 10 then true
when 11 then false
else false
print(result)

View File

@@ -6,9 +6,9 @@ _.each: obj, iterator, context =>
if obj.forEach
obj.forEach(iterator, context)
else if _.isArray(obj) or _.isArguments(obj)
iterator.call(context, item, i, obj) for item, i in obj.
iterator.call(context, item, i, obj) for item, i in obj
else
iterator.call(context, obj[key], key, obj) for key in _.keys(obj)..
iterator.call(context, obj[key], key, obj) for key in _.keys(obj)
catch e
throw e if e isnt breaker.
obj.
throw e if e isnt breaker
obj

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,16 @@
# Everything should be able to be an expression.
result: while sunny?
go_outside()
print(3 + try
nonexistent.no_way
catch error
print(error)
3
)
func: x =>
return throw x
print(x * x for x in [1..100])

View File

@@ -0,0 +1,20 @@
# test
f1: x =>
x * x
f2: y =>
y * x
f3: 3
# Parens can close on the proper level.
elements.each(el =>
el.click(event =>
el.reset()
el.show() if event.active
)
)
# Or, parens can close blocks early.
elements.each(el =>
el.click(event =>
el.reset()
el.show() if event.active))

View File

@@ -2,27 +2,35 @@ require 'test_helper'
class ExecutionTest < Test::Unit::TestCase
NO_WARNINGS = /\A(0 error\(s\), 0 warning\(s\)\n)+\Z/
ALLS_WELL = /\A\n?(true\n)+\Z/m
NO_WARNINGS = "0 error(s), 0 warning(s)"
# This is by far the most important test. It evaluates all of the
# CoffeeScript in test/fixtures/execution, ensuring that all our
# syntax actually works.
def test_execution_of_coffeescript
sources = ['test/fixtures/execution/*.coffee'].join(' ')
assert `bin/coffee -r #{sources}`.match(ALLS_WELL)
(`bin/coffee -r #{sources}`).split("\n").each do |line|
assert line == "true"
end
end
def test_lintless_coffeescript
lint_results = `bin/coffee -l test/fixtures/execution/*.coffee`
assert lint_results.match(NO_WARNINGS)
def test_lintless_tests
no_warnings `bin/coffee -l test/fixtures/*/*.coffee`
end
def test_lintless_examples
lint_results = `bin/coffee -l examples/*.coffee`
assert lint_results.match(NO_WARNINGS)
no_warnings `bin/coffee -l examples/*.coffee`
end
def test_lintless_documentation
lint_results = `bin/coffee -l documentation/coffee/*.coffee`
assert lint_results.match(NO_WARNINGS)
no_warnings `bin/coffee -l documentation/coffee/*.coffee`
end
private
def no_warnings(output)
output.split("\n").each {|line| assert line == NO_WARNINGS }
end
end

View File

@@ -7,45 +7,47 @@ class LexerTest < Test::Unit::TestCase
end
def test_lexing_an_empty_string
assert @lex.tokenize("") == []
assert @lex.tokenize("") == [["\n", "\n"]]
end
def test_lexing_basic_assignment
code = "a: 'one'; b: [1, 2]"
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"],
[:STRING, "'one'"], [";", ";"], [:IDENTIFIER, "b"], [:ASSIGN, ":"],
["[", "["], [:NUMBER, "1"], [",", ","], [:NUMBER, "2"], ["]", "]"]]
code = "a: 'one'\nb: [1, 2]"
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"],
[:STRING, "'one'"], ["\n", "\n"], [:IDENTIFIER, "b"], [:ASSIGN, ":"],
["[", "["], [:NUMBER, "1"], [",", ","], [:NUMBER, "2"], ["]", "]"],
["\n", "\n"]]
end
def test_lexing_object_literal
code = "{one : 1}"
assert @lex.tokenize(code) == [["{", "{"], [:IDENTIFIER, "one"], [:ASSIGN, ":"],
[:NUMBER, "1"], ["}", "}"]]
[:NUMBER, "1"], ["}", "}"], ["\n", "\n"]]
end
def test_lexing_function_definition
code = "x, y => x * y."
code = "x, y => x * y"
assert @lex.tokenize(code) == [[:PARAM, "x"], [",", ","], [:PARAM, "y"],
["=>", "=>"], [:IDENTIFIER, "x"], ["*", "*"], [:IDENTIFIER, "y"], [".", "."]]
["=>", "=>"], [:INDENT, 2], [:IDENTIFIER, "x"], ["*", "*"],
[:IDENTIFIER, "y"], [:OUTDENT, 2], ["\n", "\n"]]
end
def test_lexing_if_statement
code = "clap_your_hands() if happy"
assert @lex.tokenize(code) == [[:IDENTIFIER, "clap_your_hands"], ["(", "("],
[")", ")"], [:IF, "if"], [:IDENTIFIER, "happy"]]
[")", ")"], [:IF, "if"], [:IDENTIFIER, "happy"], ["\n", "\n"]]
end
def test_lexing_comment
code = "a: 1\n # comment\n # on two lines\nb: 2"
code = "a: 1\n# comment\n# on two lines\nb: 2"
assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"], [:NUMBER, "1"],
["\n", "\n"], [:COMMENT, [" comment", " on two lines"]], ["\n", "\n"],
[:IDENTIFIER, "b"], [:ASSIGN, ":"], [:NUMBER, "2"]]
[:IDENTIFIER, "b"], [:ASSIGN, ":"], [:NUMBER, "2"], ["\n", "\n"]]
end
def test_lexing_newline_escaper
code = "two: 1 + \\\n\n 1"
assert @lex.tokenize(code) == [[:IDENTIFIER, "two"], [:ASSIGN, ":"],
[:NUMBER, "1"], ["+", "+"], [:NUMBER, "1"]]
[:NUMBER, "1"], ["+", "+"], [:NUMBER, "1"], ["\n", "\n"]]
end
def test_lexing

View File

@@ -8,7 +8,7 @@ class ParserTest < Test::Unit::TestCase
def test_parsing_an_empty_string
nodes = @par.parse("")
assert nodes.is_a? Expressions
assert nodes.is_a?(Expressions)
assert nodes.expressions.empty?
end
@@ -16,45 +16,45 @@ class ParserTest < Test::Unit::TestCase
nodes = @par.parse("a: 'one'").expressions
assert nodes.length == 1
assign = nodes.first
assert assign.is_a? AssignNode
assert assign.is_a?(AssignNode)
assert assign.variable.literal == 'a'
end
def test_parsing_an_object_literal
nodes = @par.parse("{one : 1 \n two : 2}").expressions
nodes = @par.parse("{one : 1\ntwo : 2}").expressions
obj = nodes.first.literal
assert obj.is_a? ObjectNode
assert obj.is_a?(ObjectNode)
assert obj.properties.first.variable.literal.value == "one"
assert obj.properties.last.variable.literal.value == "two"
end
def test_parsing_an_function_definition
code = @par.parse("x, y => x * y.").expressions.first
code = @par.parse("x, y => x * y").expressions.first
assert code.params == ['x', 'y']
body = code.body.expressions.first
assert body.is_a? OpNode
assert body.is_a?(OpNode)
assert body.operator == '*'
end
def test_parsing_if_statement
the_if = @par.parse("clap_your_hands() if happy").expressions.first
assert the_if.is_a? IfNode
assert the_if.is_a?(IfNode)
assert the_if.condition.literal == 'happy'
assert the_if.body.is_a? CallNode
assert the_if.body.is_a?(CallNode)
assert the_if.body.variable.literal == 'clap_your_hands'
end
def test_parsing_array_comprehension
nodes = @par.parse("i for x, i in [10, 9, 8, 7, 6, 5] if i % 2 is 0.").expressions
assert nodes.first.is_a? ForNode
nodes = @par.parse("i for x, i in [10, 9, 8, 7, 6, 5] when i % 2 is 0").expressions
assert nodes.first.is_a?(ForNode)
assert nodes.first.body.literal == 'i'
assert nodes.first.filter.operator == '==='
assert nodes.first.source.literal.objects.last.literal.value == "5"
end
def test_parsing_comment
nodes = @par.parse("a: 1\n # comment\nb: 2").expressions
assert nodes[1].is_a? CommentNode
nodes = @par.parse("a: 1\n# comment\nb: 2").expressions
assert nodes[1].is_a?(CommentNode)
end
def test_parsing_inner_comments
@@ -65,9 +65,9 @@ class ParserTest < Test::Unit::TestCase
def test_parsing
nodes = @par.parse(File.read('test/fixtures/generation/each.coffee'))
assign = nodes.expressions[1]
assert assign.is_a? AssignNode
assert assign.is_a?(AssignNode)
assert assign.variable.literal == '_'
assert assign.value.is_a? CodeNode
assert assign.value.is_a?(CodeNode)
assert assign.value.params == ['obj', 'iterator', 'context']
assert nodes.compile == File.read('test/fixtures/generation/each.js')
end
@@ -77,10 +77,4 @@ class ParserTest < Test::Unit::TestCase
assert nodes.compile(:no_wrap => true) == File.read('test/fixtures/generation/each_no_wrap.js')
end
def test_no_wrapping_parens_around_statements
assert_raises(SyntaxError) do
@par.parse("(try thing() catch error fail().)").compile
end
end
end