Merge branch 'master' of http://github.com/jashkenas/coffee-script into refactorTests

This commit is contained in:
Michael Ficarra
2010-12-05 22:31:28 -05:00
15 changed files with 115 additions and 76 deletions

View File

@@ -1,7 +1,2 @@
# Eat lunch.
lunch = eat food for food in ['toast', 'cheese', 'wine']
# Naive collision detection.
for roid, pos in asteroids
for roid2 in asteroids when roid isnt roid2
roid.explode() if roid.overlaps roid2
eat food for food in ['toast', 'cheese', 'wine']

View File

@@ -517,6 +517,15 @@ coffee --bare --print --stdio</pre>
end of your comprehension.
</p>
<%= code_for('range_comprehensions', 'countdown') %>
<p>
Note how because we are assigning the value of the comprehensions to a
variable in the example above, CoffeeScript is collecting the result of
each iteration into an array. Sometimes functions end with loops that are
intended to run only for their side-effects. Be careful that you're not
accidentally returning the results of the comprehension in these cases,
by adding a meaningful return value, like <tt>true</tt>, or <tt>null</tt>,
to the bottom of your function.
</p>
<p>
Comprehensions can also be used to iterate over the keys and values in
an object. Use <tt>of</tt> to signal comprehension over the properties of

View File

@@ -1,17 +1,6 @@
var food, lunch, pos, roid, roid2, _i, _j, _len, _len2, _len3, _ref;
var food, _i, _len, _ref;
_ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
food = _ref[_i];
lunch = eat(food);
}
for (pos = 0, _len2 = asteroids.length; pos < _len2; pos++) {
roid = asteroids[pos];
for (_j = 0, _len3 = asteroids.length; _j < _len3; _j++) {
roid2 = asteroids[_j];
if (roid !== roid2) {
if (roid.overlaps(roid2)) {
roid.explode();
}
}
}
eat(food);
}

View File

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

View File

@@ -854,28 +854,12 @@ lyrics = function() {
would use a loop, <b>each</b>/<b>forEach</b>, <b>map</b>, or <b>select</b>/<b>filter</b>.
</p>
<div class='code'><pre class="idle"><span class="Comment"><span class="Comment">#</span> Eat lunch.</span>
lunch <span class="Keyword">=</span> eat food <span class="Keyword">for</span> food <span class="Keyword">in</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>]
<span class="Comment"><span class="Comment">#</span> Naive collision detection.</span>
<span class="Keyword">for</span> roid, pos <span class="Keyword">in</span> asteroids
<span class="Keyword">for</span> roid2 <span class="Keyword">in</span> asteroids <span class="Keyword">when</span> roid <span class="Keyword">isnt</span> roid2
roid.explode() <span class="Keyword">if</span> roid.overlaps roid2
</pre><pre class="idle"><span class="Storage">var</span> food, lunch, pos, roid, roid2, _i, _j, _len, _len2, _len3, _ref;
eat food <span class="Keyword">for</span> food <span class="Keyword">in</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>]
</pre><pre class="idle"><span class="Storage">var</span> food, _i, _len, _ref;
_ref <span class="Keyword">=</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>];
<span class="Keyword">for</span> (_i <span class="Keyword">=</span> <span class="Number">0</span>, _len <span class="Keyword">=</span> _ref.<span class="LibraryConstant">length</span>; _i <span class="Keyword">&lt;</span> _len; _i<span class="Keyword">++</span>) {
food <span class="Keyword">=</span> _ref[_i];
lunch <span class="Keyword">=</span> eat(food);
}
<span class="Keyword">for</span> (pos <span class="Keyword">=</span> <span class="Number">0</span>, _len2 <span class="Keyword">=</span> asteroids.<span class="LibraryConstant">length</span>; pos <span class="Keyword">&lt;</span> _len2; pos<span class="Keyword">++</span>) {
roid <span class="Keyword">=</span> asteroids[pos];
<span class="Keyword">for</span> (_j <span class="Keyword">=</span> <span class="Number">0</span>, _len3 <span class="Keyword">=</span> asteroids.<span class="LibraryConstant">length</span>; _j <span class="Keyword">&lt;</span> _len3; _j<span class="Keyword">++</span>) {
roid2 <span class="Keyword">=</span> asteroids[_j];
<span class="Keyword">if</span> (roid <span class="Keyword">!</span><span class="Keyword">==</span> roid2) {
<span class="Keyword">if</span> (roid.overlaps(roid2)) {
roid.explode();
}
}
}
eat(food);
}
</pre><br class='clear' /></div>
<p>
@@ -901,6 +885,15 @@ countdown = (function() {
}
return _results;
}());;alert(countdown);'>run: countdown</button><br class='clear' /></div>
<p>
Note how because we are assigning the value of the comprehensions to a
variable in the example above, CoffeeScript is collecting the result of
each iteration into an array. Sometimes functions end with loops that are
intended to run only for their side-effects. Be careful that you're not
accidentally returning the results of the comprehension in these cases,
by adding a meaningful return value, like <tt>true</tt>, or <tt>null</tt>,
to the bottom of your function.
</p>
<p>
Comprehensions can also be used to iterate over the keys and values in
an object. Use <tt>of</tt> to signal comprehension over the properties of
@@ -975,12 +968,12 @@ middle = copy.slice(3, 6 + 1);;alert(middle);'>run: middle</button><br class='cl
numbers[<span class="Number">3</span>..<span class="Number">6</span>] <span class="Keyword">=</span> [<span class="Keyword">-</span><span class="Number">3</span>, <span class="Keyword">-</span><span class="Number">4</span>, <span class="Keyword">-</span><span class="Number">5</span>, <span class="Keyword">-</span><span class="Number">6</span>]
</pre><pre class="idle"><span class="Storage">var</span> numbers, _ref;
</pre><pre class="idle"><span class="Storage">var</span> numbers;
numbers <span class="Keyword">=</span> [<span class="Number">0</span>, <span class="Number">1</span>, <span class="Number">2</span>, <span class="Number">3</span>, <span class="Number">4</span>, <span class="Number">5</span>, <span class="Number">6</span>, <span class="Number">7</span>, <span class="Number">8</span>, <span class="Number">9</span>];
([].splice.<span class="LibraryFunction">apply</span>(numbers, [<span class="Number">3</span>, <span class="Number">6</span> <span class="Keyword">-</span> <span class="Number">3</span> <span class="Keyword">+</span> <span class="Number">1</span>].<span class="LibraryFunction">concat</span>(_ref <span class="Keyword">=</span> [<span class="Keyword">-</span><span class="Number">3</span>, <span class="Keyword">-</span><span class="Number">4</span>, <span class="Keyword">-</span><span class="Number">5</span>, <span class="Keyword">-</span><span class="Number">6</span>])), _ref);
</pre><button onclick='javascript: var numbers, _ref;
[].splice.<span class="LibraryFunction">apply</span>(numbers, [<span class="Number">3</span>, <span class="Number">4</span>].<span class="LibraryFunction">concat</span>([<span class="Keyword">-</span><span class="Number">3</span>, <span class="Keyword">-</span><span class="Number">4</span>, <span class="Keyword">-</span><span class="Number">5</span>, <span class="Keyword">-</span><span class="Number">6</span>]));
</pre><button onclick='javascript: var numbers;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
([].splice.apply(numbers, [3, 6 - 3 + 1].concat(_ref = [-3, -4, -5, -6])), _ref);;alert(numbers);'>run: numbers</button><br class='clear' /></div>
[].splice.apply(numbers, [3, 4].concat([-3, -4, -5, -6]));;alert(numbers);'>run: numbers</button><br class='clear' /></div>
<p>
Note that JavaScript strings are immutable, and can't be spliced.
</p>
@@ -1920,7 +1913,7 @@ task(<span class="String"><span class="String">'</span>build:parser<span class="
works like <tt>null</tt>, and cannot be assigned a new value.
There was a precedence change with respect to single-line comprehensions:
<tt>result = i for i in list</tt><br /> used to parse as <tt>result = (i for i in list)</tt>
by default ... it now parses as <br /ea><tt>(result = i) for i in list</tt>.
by default ... it now parses as <br /><tt>(result = i) for i in list</tt>.
</p>
<p>

View File

@@ -1,7 +1,8 @@
(function() {
var ALL_SWITCHES, BANNER, CoffeeScript, DEPRECATED_SWITCHES, EventEmitter, SWITCHES, compileOptions, compileScript, compileScripts, compileStdio, exec, fs, helpers, lint, optionParser, optparse, opts, parseOptions, path, printLine, printTokens, printWarn, sources, spawn, usage, version, watch, writeJs, _ref;
var ALL_SWITCHES, BANNER, CoffeeScript, DEPRECATED_SWITCHES, EventEmitter, SWITCHES, compileOptions, compileScript, compileScripts, compileStdio, exec, fs, helpers, lint, optionParser, optparse, opts, parseOptions, path, printLine, printTokens, printWarn, sources, spawn, usage, util, version, watch, writeJs, _ref;
fs = require('fs');
path = require('path');
util = require('util');
helpers = require('./helpers');
optparse = require('./optparse');
CoffeeScript = require('./coffee-script');
@@ -55,7 +56,7 @@
return compileScripts();
};
compileScripts = function() {
var base, compile, _fn, _i, _len, _results;
var base, compile, source, _fn, _i, _len, _results;
_fn = function(source) {
base = path.join(source);
compile = function(source, topLevel) {
@@ -185,7 +186,7 @@
if (err) {
return printLine(err.message);
} else if (opts.compile && opts.watch) {
return printLine("Compiled " + source);
return util.log("compiled " + source);
}
});
};

View File

@@ -520,7 +520,13 @@
__extends(Call, Base);
Call.prototype.children = ['variable', 'args'];
Call.prototype.newInstance = function() {
this.isNew = true;
var base;
base = this.variable.base || this.variable;
if (base instanceof Call) {
base.newInstance();
} else {
this.isNew = true;
}
return this;
};
Call.prototype.superReference = function(o) {
@@ -1198,9 +1204,12 @@
return !!this.ctor;
};
Code.prototype.compileNode = function(o) {
var code, exprs, i, idt, lit, p, param, ref, scope, sharedScope, splats, v, val, vars, wasEmpty, _i, _j, _k, _len, _len2, _len3, _len4, _ref, _ref2, _ref3, _ref4, _results;
var code, exprs, i, idt, lit, p, param, ref, sharedScope, splats, v, val, vars, wasEmpty, _i, _j, _k, _len, _len2, _len3, _len4, _ref, _ref2, _ref3, _ref4, _results;
sharedScope = del(o, 'sharedScope');
o.scope = scope = sharedScope || new Scope(o.scope, this.body, this);
o.scope = sharedScope || new Scope(o.scope, this.body, this);
if (sharedScope) {
o.scope.shared = true;
}
o.indent += TAB;
delete o.bare;
delete o.globals;
@@ -1253,7 +1262,7 @@
if (!splats) {
for (i = 0, _len4 = vars.length; i < _len4; i++) {
v = vars[i];
scope.parameter(vars[i] = v.compile(o));
o.scope.parameter(vars[i] = v.compile(o));
}
}
if (!(wasEmpty || this.noReturn)) {
@@ -1763,17 +1772,15 @@
scope = o.scope;
name = this.name && this.name.compile(o, LEVEL_LIST);
index = this.index && this.index.compile(o, LEVEL_LIST);
if (!hasCode) {
if (name && !this.pattern) {
scope.find(name, {
immediate: true
});
}
if (index) {
scope.find(index, {
immediate: true
});
}
if (name && !this.pattern) {
scope.find(name, {
immediate: true
});
}
if (index) {
scope.find(index, {
immediate: true
});
}
if (this.returns && !hasPure) {
rvar = scope.freeVariable('results');

View File

@@ -1,9 +1,12 @@
(function() {
var CoffeeScript, helpers, readline, repl, run, stdio;
var CoffeeScript, error, helpers, readline, repl, run, stdio;
CoffeeScript = require('./coffee-script');
helpers = require('./helpers');
readline = require('readline');
stdio = process.openStdin();
error = function(err) {
return stdio.write((err.stack || err.toString()) + '\n\n');
};
helpers.extend(global, {
quit: function() {
return process.exit(0);
@@ -21,10 +24,11 @@
console.log(val);
}
} catch (err) {
console.error(err.stack || err.toString());
error(err);
}
return repl.prompt();
};
process.on('uncaughtException', error);
repl = readline.createInterface(stdio);
repl.setPrompt('coffee> ');
stdio.on('data', function(buffer) {

View File

@@ -48,6 +48,9 @@
return false;
};
Scope.prototype.parameter = function(name) {
if (this.shared && this.check(name, true)) {
return;
}
return this.add(name, 'param');
};
Scope.prototype.check = function(name, immediate) {

View File

@@ -7,6 +7,7 @@
# External dependencies.
fs = require 'fs'
path = require 'path'
util = require 'util'
helpers = require './helpers'
optparse = require './optparse'
CoffeeScript = require './coffee-script'
@@ -154,7 +155,7 @@ writeJs = (source, js, base) ->
js = ' ' if js.length <= 0
fs.writeFile jsPath, js, (err) ->
if err then printLine err.message
else if opts.compile and opts.watch then printLine "Compiled #{source}"
else if opts.compile and opts.watch then util.log "compiled #{source}"
path.exists dir, (exists) ->
if exists then compile() else exec "mkdir -p #{dir}", compile

View File

@@ -433,7 +433,11 @@ exports.Call = class Call extends Base
# Tag this invocation as creating a new instance.
newInstance: ->
@isNew = true
base = @variable.base or @variable
if base instanceof Call
base.newInstance()
else
@isNew = true
this
# Grab the reference to the superclass's implementation of the current
@@ -978,9 +982,10 @@ exports.Code = class Code extends Base
# arrow, generates a wrapper that saves the current value of `this` through
# a closure.
compileNode: (o) ->
sharedScope = del o, 'sharedScope'
o.scope = scope = sharedScope or new Scope o.scope, @body, this
o.indent += TAB
sharedScope = del o, 'sharedScope'
o.scope = sharedScope or new Scope o.scope, @body, this
o.scope.shared = yes if sharedScope
o.indent += TAB
delete o.bare
delete o.globals
vars = []
@@ -1004,7 +1009,7 @@ exports.Code = class Code extends Base
wasEmpty = @body.isEmpty()
exprs.unshift splats if splats
@body.expressions.unshift exprs... if exprs.length
scope.parameter vars[i] = v.compile o for v, i in vars unless splats
o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
@body.makeReturn() unless wasEmpty or @noReturn
idt = o.indent
code = 'function'
@@ -1421,9 +1426,8 @@ exports.For = class For extends Base
scope = o.scope
name = @name and @name.compile o, LEVEL_LIST
index = @index and @index.compile o, LEVEL_LIST
unless hasCode
scope.find(name, immediate: yes) if name and not @pattern
scope.find(index, immediate: yes) if index
scope.find(name, immediate: yes) if name and not @pattern
scope.find(index, immediate: yes) if index
rvar = scope.freeVariable 'results' if @returns and not hasPure
ivar = (if @range then name else index) or scope.freeVariable 'i'
varPart = ''

View File

@@ -12,6 +12,10 @@ readline = require 'readline'
# Start by opening up **stdio**.
stdio = process.openStdin()
# Log an error.
error = (err) ->
stdio.write (err.stack or err.toString()) + '\n\n'
# Quick alias for quitting the REPL.
helpers.extend global, quit: -> process.exit(0)
@@ -23,9 +27,12 @@ run = (buffer) ->
val = CoffeeScript.eval buffer.toString(), bare: on, globals: on, fileName: 'repl'
console.log val if val isnt undefined
catch err
console.error err.stack or err.toString()
error err
repl.prompt()
# Make sure that uncaught exceptions don't kill the REPL.
process.on 'uncaughtException', error
# Create the REPL by listening to **stdin**.
repl = readline.createInterface stdio
repl.setPrompt 'coffee> '

View File

@@ -44,6 +44,7 @@ exports.Scope = class Scope
# Reserve a variable name as originating from a function parameter for this
# scope. No `var` required for internal references.
parameter: (name) ->
return if @shared and @check name, yes
@add name, 'param'
# Just check to see if a variable has already been declared, without reserving,

View File

@@ -227,4 +227,17 @@ foo = ->
for j in [0..7]
-> i + j
eq foo()[3][4](), 7
eq foo()[3][4](), 7
# Issue #897: Ensure that plucked function variables aren't leaked.
facets = {}
list = ['one', 'two']
(->
for entity in list
facets[entity] = -> entity
)()
eq typeof entity, 'undefined'
eq facets['two'](), 'two'

View File

@@ -346,6 +346,7 @@ eq ok, new ->
### Should `return` implicitly ###
### even with trailing comments. ###
#855: execution context for `func arr...` should be `null`
(->
global = @
@@ -355,3 +356,14 @@ eq ok, new ->
contextTest.apply null, array
contextTest array...
)()
# #894: Splatting against constructor-chained functions.
x = null
class Foo
bar: (y) -> x = y
new Foo().bar([101]...)
eq x, 101