merging in master

This commit is contained in:
Jeremy Ashkenas
2009-12-28 20:06:23 -05:00
21 changed files with 295 additions and 299 deletions

View File

@@ -19,7 +19,8 @@ namespace :build do
desc "Compile the Narwhal interface for --interactive and --run" desc "Compile the Narwhal interface for --interactive and --run"
task :narwhal do 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 end
end end

View File

@@ -1,7 +1,7 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = 'coffee-script' s.name = 'coffee-script'
s.version = '0.1.4' # Keep version in sync with coffee-script.rb s.version = '0.1.6' # Keep version in sync with coffee-script.rb
s.date = '2009-12-25' s.date = '2009-12-27'
s.homepage = "http://jashkenas.github.com/coffee-script/" s.homepage = "http://jashkenas.github.com/coffee-script/"
s.summary = "The CoffeeScript Compiler" s.summary = "The CoffeeScript Compiler"
@@ -22,5 +22,5 @@ Gem::Specification.new do |s|
s.require_paths = ['lib'] s.require_paths = ['lib']
s.executables = ['coffee'] 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 end

View File

@@ -357,6 +357,11 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
would use a loop, <b>each</b>/<b>forEach</b>, <b>map</b>, or <b>select</b>/<b>filter</b>. would use a loop, <b>each</b>/<b>forEach</b>, <b>map</b>, or <b>select</b>/<b>filter</b>.
</p> </p>
<%= code_for('array_comprehensions') %> <%= code_for('array_comprehensions') %>
<p>
If you're not iterating over an actual array, you can use a range to
specify the start and end of an array comprehension:
<tt>coundown(i) for i in [10..1].</tt>
</p>
<p id="slice"> <p id="slice">
<b class="header">Slicing Arrays with Ranges</b> <b class="header">Slicing Arrays with Ranges</b>
@@ -464,6 +469,22 @@ coffee --print app/scripts/*.coffee > concatenation.js</pre>
</ul> </ul>
<h2 id="change_log">Change Log</h2> <h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 20px;">0.1.6</b>
Bugfix for running <tt>coffee --interactive</tt> and <tt>--run</tt>
from outside of the CoffeeScript directory. Bugfix for nested
function/if-statements.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.5</b>
Array slice literals and array comprehensions can now both take Ruby-style
ranges to specify the start and end. JavaScript variable declaration is
now pushed up to the top of the scope, making all assignment statements into
expressions. You can use <tt>\</tt> to escape newlines.
The <tt>coffee-script</tt> command is now called <tt>coffee</tt>.
</p>
<p> <p>
<b class="header" style="margin-top: 20px;">0.1.4</b> <b class="header" style="margin-top: 20px;">0.1.4</b>

View File

@@ -1,5 +1,5 @@
(function(){ (function(){
var __a, __b, __c, __d, __e, __f, __g, __h, __i, __j, __k, __l, food, i, lunch, row; var __a, __b, __c, __d, __e, __f, __g, __h, food, i, lunch, row;
// Eat lunch. // Eat lunch.
__a = ['toast', 'cheese', 'wine']; __a = ['toast', 'cheese', 'wine'];
__d = []; __d = [];
@@ -17,10 +17,4 @@
__h[__f] = i % 2 === 0 ? highlight(row) : null; __h[__f] = i % 2 === 0 ? highlight(row) : null;
} }
__h; __h;
// Check the first one hundred combinations.
__k = [];
for (__l=0, i=1, __j=100; i<=__j; i++, __l++) {
__k[__l] = lockpick(combinations[i]);
}
__k;
})(); })();

View File

@@ -4,8 +4,7 @@
change_numbers = function() { change_numbers = function() {
var new_num; var new_num;
num = 2; num = 2;
new_num = 3; return (new_num = 3);
return new_num;
}; };
new_num = change_numbers(); new_num = change_numbers();
})(); })();

View File

@@ -6,8 +6,7 @@
return alert(this.name + " moved " + meters + "m."); return alert(this.name + " moved " + meters + "m.");
}; };
Snake = function(name) { Snake = function(name) {
this.name = name; return (this.name = name);
return this.name;
}; };
Snake.__superClass__ = Animal.prototype; Snake.__superClass__ = Animal.prototype;
Snake.prototype = new Animal(); Snake.prototype = new Animal();
@@ -17,8 +16,7 @@
return Snake.__superClass__.move.call(this, 5); return Snake.__superClass__.move.call(this, 5);
}; };
Horse = function(name) { Horse = function(name) {
this.name = name; return (this.name = name);
return this.name;
}; };
Horse.__superClass__ = Animal.prototype; Horse.__superClass__ = Animal.prototype;
Horse.prototype = new Animal(); Horse.prototype = new Animal();

View File

@@ -93,77 +93,63 @@ _.reduceRight: obj, memo, 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: [] results: []
_.each(obj, (value, index, list => _.each(obj, (value, index, list =>
iterator.call(context, value, index, list) and results.push(value).)) results.push(value) if iterator.call(context, value, index, list).))
results. results.
# # Return all the elements for which a truth test fails.
# # Return all the elements for which a truth test fails. _.reject: obj, iterator, context =>
# _.reject = function(obj, iterator, context) { results: []
# var results = []; _.each(obj, (value, index, list =>
# _.each(obj, function(value, index, list) { results.push(value) if not iterator.call(context, value, index, list).))
# !iterator.call(context, value, index, list) && results.push(value); results.
# });
# return results; # Determine whether all of the elements match a truth test. Delegate to
# }; # JavaScript 1.6's every(), if it is present.
# _.all: obj, iterator, context =>
# # Determine whether all of the elements match a truth test. Delegate to iterator ||= _.identity
# # JavaScript 1.6's every(), if it is present. return obj.every(iterator, context) if obj and _.isFunction(obj.every)
# _.all = function(obj, iterator, context) { result: true
# iterator = iterator || _.identity; _.each(obj, (value, index, list =>
# if (obj && _.isFunction(obj.every)) return obj.every(iterator, context); _.breakLoop() unless result: result and iterator.call(context, value, index, list).))
# var result = true; result.
# _.each(obj, function(value, index, list) {
# if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop(); # Determine if at least one element in the object matches a truth test. Use
# }); # JavaScript 1.6's some(), if it exists.
# return result; _.any: obj, iterator, context =>
# }; iterator ||= _.identity
# return obj.some(iterator, context) if obj and _.isFunction(obj.some)
# # Determine if at least one element in the object matches a truth test. Use result: false
# # JavaScript 1.6's some(), if it exists. _.each(obj, (value, index, list =>
# _.any = function(obj, iterator, context) { _.breakLoop() if (result: iterator.call(context, value, index, list)).))
# iterator = iterator || _.identity; result.
# if (obj && _.isFunction(obj.some)) return obj.some(iterator, context);
# var result = false; # Determine if a given value is included in the array or object,
# _.each(obj, function(value, index, list) { # based on '==='.
# if (result = iterator.call(context, value, index, list)) _.breakLoop(); _.include: obj, target =>
# }); return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
# return result; found: false
# }; _.each(obj, (value =>
# _.breakLoop() if (found: value is target).))
# # Determine if a given value is included in the array or object, found.
# # based on '==='.
# _.include = function(obj, target) { # Invoke a method with arguments on every item in a collection.
# if (_.isArray(obj)) return _.indexOf(obj, target) != -1; _.invoke: obj, method =>
# var found = false; args: _.rest(arguments, 2)
# _.each(obj, function(value) { _.map(obj, (value =>
# if (found = value === target) _.breakLoop(); (if method then value[method] else value.).apply(value, args).)).
# });
# return found; # Convenience version of a common use case of map: fetching a property.
# }; _.pluck: obj, key =>
# _.map(obj, (value => value[key].)).
# # Invoke a method with arguments on every item in a collection.
# _.invoke = function(obj, method) { # Return the maximum item or (item-based computation).
# var args = _.rest(arguments, 2); _.max: obj, iterator, context =>
# return _.map(obj, function(value) { return Math.max.apply(Math, obj) if !iterator and _.isArray(obj)
# return (method ? value[method] : value).apply(value, args); 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}).))
# # Convenience version of a common use case of map: fetching a property. result.value.
# _.pluck = function(obj, key) {
# return _.map(obj, function(value){ return value[key]; });
# };
#
# # Return the maximum item or (item-based computation).
# _.max = function(obj, iterator, context) {
# if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
# var result = {computed : -Infinity};
# _.each(obj, function(value, index, list) {
# var computed = iterator ? iterator.call(context, value, index, list) : value;
# computed >= result.computed && (result = {value : value, computed : computed});
# });
# return result.value;
# };
# #
# # Return the minimum element (or element-based computation). # # Return the minimum element (or element-based computation).
# _.min = function(obj, iterator, context) { # _.min = function(obj, iterator, context) {
@@ -320,18 +306,15 @@ _.reduceRight: obj, memo, iterator, context =>
# range[idx++] = i; # range[idx++] = i;
# } # }
# }; # };
#
# /* ----------------------- Function Functions: -----------------------------*/ # ----------------------- Function Functions: -----------------------------
#
# # Create a function bound to a given object (assigning 'this', and arguments, # Create a function bound to a given object (assigning 'this', and arguments,
# # optionally). Binding with arguments is also known as 'curry'. # optionally). Binding with arguments is also known as 'curry'.
# _.bind = function(func, obj) { _.bind: func, obj =>
# var args = _.rest(arguments, 2); args: _.rest(arguments, 2)
# return function() { => func.apply(obj or root, args.concat(_.toArray(arguments)))..
# return func.apply(obj || root, args.concat(_.toArray(arguments)));
# };
# };
#
# # Bind all of an object's methods to that object. Useful for ensuring that # # Bind all of an object's methods to that object. Useful for ensuring that
# # all callbacks defined on an object belong to it. # # all callbacks defined on an object belong to it.
# _.bindAll = function(obj) { # _.bindAll = function(obj) {
@@ -347,36 +330,27 @@ _.reduceRight: obj, memo, iterator, context =>
# var args = _.rest(arguments, 2); # var args = _.rest(arguments, 2);
# return setTimeout(function(){ return func.apply(func, args); }, wait); # return setTimeout(function(){ return func.apply(func, args); }, wait);
# }; # };
#
# # Defers a function, scheduling it to run after the current call stack has # Defers a function, scheduling it to run after the current call stack has
# # cleared. # cleared.
# _.defer = function(func) { _.defer: func =>
# return _.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,
# # Returns the first function passed as an argument to the second, # allowing you to adjust arguments, run code before and after, and
# # allowing you to adjust arguments, run code before and after, and # conditionally execute the original function.
# # conditionally execute the original function. _.wrap: func, wrapper =>
# _.wrap = function(func, wrapper) { => wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))..
# return function() {
# var args = [func].concat(_.toArray(arguments)); # Returns a function that is the composition of a list of functions, each
# return wrapper.apply(wrapper, args); # consuming the return value of the function that follows.
# }; _.compose: =>
# }; funcs: _.toArray(arguments)
# =>
# # Returns a function that is the composition of a list of functions, each args: _.toArray(arguments)
# # consuming the return value of the function that follows. args: [funcs[i]].apply(this, args) for i in [(funcs.length - 1)..0].
# _.compose = function() { args[0]..
# var funcs = _.toArray(arguments);
# return function() {
# var args = _.toArray(arguments);
# for (var i=funcs.length-1; i >= 0; i--) {
# args = [funcs[i].apply(this, args)];
# }
# return args[0];
# };
# };
#
# /* ------------------------- Object Functions: ---------------------------- */ # /* ------------------------- Object Functions: ---------------------------- */
# #
# # Retrieve the names of an object's properties. # # Retrieve the names of an object's properties.
@@ -408,81 +382,67 @@ _.reduceRight: obj, memo, iterator, context =>
# if (_.isArray(obj)) return obj.slice(0); # if (_.isArray(obj)) return obj.slice(0);
# return _.extend({}, obj); # return _.extend({}, obj);
# }; # };
#
# # Perform a deep comparison to check if two objects are equal. # Perform a deep comparison to check if two objects are equal.
# _.isEqual = function(a, b) { _.isEqual: a, b =>
# # Check object identity. # Check object identity.
# if (a === b) return true; return true if a is b
# # Different types? # Different types?
# var atype = typeof(a), btype = typeof(b); atype: typeof(a); btype: typeof(b)
# if (atype != btype) return false; return false if atype isnt btype
# # Basic equality test (watch out for coercions). # Basic equality test (watch out for coercions).
# if (a == b) return true; return true if `a == b`
# # One is falsy and the other truthy. # One is falsy and the other truthy.
# if ((!a && b) || (a && !b)) return false; return false if (!a and b) or (a and !b)
# # One of them implements an isEqual()? # One of them implements an isEqual()?
# if (a.isEqual) return a.isEqual(b); return a.isEqual(b) if a.isEqual
# # Check dates' integer values. # Check dates' integer values.
# if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
# # Both are NaN? # Both are NaN?
# if (_.isNaN(a) && _.isNaN(b)) return true; return true if _.isNaN(a) and _.isNaN(b)
# # Compare regular expressions. # Compare regular expressions.
# if (_.isRegExp(a) && _.isRegExp(b)) if _.isRegExp(a) and _.isRegExp(b)
# return a.source === b.source && return a.source is b.source and \
# a.global === b.global && a.global is b.global and \
# a.ignoreCase === b.ignoreCase && a.ignoreCase is b.ignoreCase and \
# a.multiline === b.multiline; a.multiline is b.multiline.
# # If a is not an object by this point, we can't handle it. # If a is not an object by this point, we can't handle it.
# if (atype !== 'object') return false; return false if atype isnt 'object'
# # Check for different array lengths before comparing contents. # Check for different array lengths before comparing contents.
# if (a.length && (a.length !== b.length)) return false; return false if a.length and (a.length isnt b.length)
# # Nothing else worked, deep compare the contents. # Nothing else worked, deep compare the contents.
# var aKeys = _.keys(a), bKeys = _.keys(b); aKeys: _.keys(a); bKeys: _.keys(b)
# # Different object sizes? # Different object sizes?
# if (aKeys.length != bKeys.length) return false; return false if aKeys.length isnt bKeys.length
# # Recursive comparison of contents. # Recursive comparison of contents.
# for (var key in a) if (!_.isEqual(a[key], b[key])) return false; # for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
# return true; return true.
# };
# # Is a given array or object empty?
# # Is a given array or object empty? _.isEmpty: obj => _.keys(obj).length is 0.
# _.isEmpty = function(obj) {
# return _.keys(obj).length == 0; # Is a given value a DOM element?
# }; _.isElement: obj => !!(obj and obj.nodeType is 1).
#
# # Is a given value a DOM element? # Is a given variable an arguments object?
# _.isElement = function(obj) { _.isArguments: obj => obj and _.isNumber(obj.length) and !_.isArray(obj) and !propertyIsEnumerable.call(obj, 'length').
# return !!(obj && obj.nodeType == 1);
# }; # Is the given value NaN -- this one is interesting. NaN != NaN, and
# # isNaN(undefined) == true, so we make sure it's a number first.
# # Is a given variable an arguments object? _.isNaN: obj => _.isNumber(obj) and isNaN(obj).
# _.isArguments = function(obj) {
# return obj && _.isNumber(obj.length) && !_.isArray(obj) && !propertyIsEnumerable.call(obj, 'length'); # Is a given value equal to null?
# }; _.isNull: obj => obj is null.
#
# # Is the given value NaN -- this one is interesting. NaN != NaN, and # Is a given variable undefined?
# # isNaN(undefined) == true, so we make sure it's a number first. _.isUndefined: obj => typeof obj is 'undefined'.
# _.isNaN = function(obj) {
# return _.isNumber(obj) && isNaN(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 =>
# # Is a given value equal to null? interceptor(obj)
# _.isNull = function(obj) { obj.
# return obj === null;
# };
#
# # Is a given variable undefined?
# _.isUndefined = function(obj) {
# return typeof obj == 'undefined';
# };
#
# # Invokes interceptor with the obj, and then returns obj.
# # The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
# _.tap = function(obj, interceptor) {
# interceptor(obj);
# return obj;
# }
#
# # Define the isArray, isDate, isFunction, isNumber, isRegExp, and isString # # Define the isArray, isDate, isFunction, isNumber, isRegExp, and isString
# # functions based on their toString identifiers. # # functions based on their toString identifiers.
# var types = ['Array', 'Date', 'Function', 'Number', 'RegExp', 'String']; # var types = ['Array', 'Date', 'Function', 'Number', 'RegExp', 'String'];
@@ -492,26 +452,21 @@ _.reduceRight: obj, memo, iterator, context =>
# _['is' + types[i]] = function(obj) { return toString.call(obj) == identifier; }; # _['is' + types[i]] = function(obj) { return toString.call(obj) == identifier; };
# })(); # })();
# } # }
#
# /* -------------------------- Utility Functions: -------------------------- */ # -------------------------- Utility Functions: --------------------------
#
# # Run Underscore.js in noConflict mode, returning the '_' variable to its # Run Underscore.js in noConflict mode, returning the '_' variable to its
# # previous owner. Returns a reference to the Underscore object. # previous owner. Returns a reference to the Underscore object.
# _.noConflict = function() { _.noConflict: =>
# root._ = previousUnderscore; root._: previousUnderscore
# return this; this.
# };
# # Keep the identity function around for default iterators.
# # Keep the identity function around for default iterators. _.identity: value => value.
# _.identity = function(value) {
# return value; # Break out of the middle of an iteration.
# }; _.breakLoop: => throw breaker.
#
# # Break out of the middle of an iteration.
# _.breakLoop = function() {
# throw breaker;
# };
#
# # Generate a unique integer id (unique within the entire client session). # # Generate a unique integer id (unique within the entire client session).
# # Useful for temporary DOM ids. # # Useful for temporary DOM ids.
# var idCounter = 0; # var idCounter = 0;
@@ -537,19 +492,19 @@ _.reduceRight: obj, memo, iterator, context =>
# + "');}return p.join('');"); # + "');}return p.join('');");
# return data ? fn(data) : fn; # return data ? fn(data) : fn;
# }; # };
#
# /*------------------------------- Aliases ----------------------------------*/ # ------------------------------- Aliases ----------------------------------
#
# _.forEach = _.each; _.forEach: _.each
# _.foldl = _.inject = _.reduce; _.foldl: _.inject: _.reduce
# _.foldr = _.reduceRight; _.foldr: _.reduceRight
# _.filter = _.select; _.filter: _.select
# _.every = _.all; _.every: _.all
# _.some = _.any; _.some: _.any
# _.head = _.first; _.head: _.first
# _.tail = _.rest; _.tail: _.rest
# _.methods = _.functions; _.methods: _.functions
#
# /*------------------------ Setup the OOP Wrapper: --------------------------*/ # /*------------------------ Setup the OOP Wrapper: --------------------------*/
# #
# # Helper function to continue chaining intermediate results. # # Helper function to continue chaining intermediate results.

View File

@@ -373,8 +373,7 @@ num <span class="Keyword">=</span> <span class="Number">1</span>;
<span class="FunctionName">change_numbers</span> = <span class="Storage">function</span>() { <span class="FunctionName">change_numbers</span> = <span class="Storage">function</span>() {
<span class="Storage">var</span> new_num; <span class="Storage">var</span> new_num;
num <span class="Keyword">=</span> <span class="Number">2</span>; num <span class="Keyword">=</span> <span class="Number">2</span>;
new_num <span class="Keyword">=</span> <span class="Number">3</span>; <span class="Keyword">return</span> (new_num <span class="Keyword">=</span> <span class="Number">3</span>);
<span class="Keyword">return</span> new_num;
}; };
new_num <span class="Keyword">=</span> change_numbers(); new_num <span class="Keyword">=</span> change_numbers();
</pre><button onclick='javascript: var change_numbers, new_num, num; </pre><button onclick='javascript: var change_numbers, new_num, num;
@@ -382,8 +381,7 @@ num = 1;
change_numbers = function() { change_numbers = function() {
var new_num; var new_num;
num = 2; num = 2;
new_num = 3; return (new_num = 3);
return new_num;
}; };
new_num = change_numbers(); new_num = change_numbers();
;alert(new_num);'>run: new_num</button><br class='clear' /></div> ;alert(new_num);'>run: new_num</button><br class='clear' /></div>
@@ -581,10 +579,7 @@ lunch<span class="Keyword">:</span> food.eat() <span class="Keyword">for</span>
<span class="Comment"><span class="Comment">#</span> Zebra-stripe a table.</span> <span class="Comment"><span class="Comment">#</span> Zebra-stripe a table.</span>
highlight(row) <span class="Keyword">for</span> row, i <span class="Keyword">in</span> table <span class="Keyword">if</span> i <span class="Keyword">%</span> <span class="Number">2</span> <span class="Keyword">is</span> <span class="Number">0</span>. highlight(row) <span class="Keyword">for</span> row, i <span class="Keyword">in</span> table <span class="Keyword">if</span> i <span class="Keyword">%</span> <span class="Number">2</span> <span class="Keyword">is</span> <span class="Number">0</span>.
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, __d, __e, __f, __g, __h, food, i, lunch, row;
<span class="Comment"><span class="Comment">#</span> Check the first one hundred combinations.</span>
lockpick(combinations[i]) <span class="Keyword">for</span> i <span class="Keyword">in</span> [<span class="Number">1</span>..<span class="Number">100</span>].
</pre><pre class="idle"><span class="Storage">var</span> __a, __b, __c, __d, __e, __f, __g, __h, __i, __j, __k, __l, food, i, lunch, row;
<span class="Comment"><span class="Comment">//</span> Eat lunch.</span> <span class="Comment"><span class="Comment">//</span> Eat lunch.</span>
__a <span class="Keyword">=</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>]; __a <span class="Keyword">=</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>];
__d <span class="Keyword">=</span> []; __d <span class="Keyword">=</span> [];
@@ -602,13 +597,12 @@ __h <span class="Keyword">=</span> [];
__h[__f] <span class="Keyword">=</span> i <span class="Keyword">%</span> <span class="Number">2</span> <span class="Keyword">===</span> <span class="Number">0</span> ? highlight(row) : <span class="BuiltInConstant">null</span>; __h[__f] <span class="Keyword">=</span> i <span class="Keyword">%</span> <span class="Number">2</span> <span class="Keyword">===</span> <span class="Number">0</span> ? highlight(row) : <span class="BuiltInConstant">null</span>;
} }
__h; __h;
<span class="Comment"><span class="Comment">//</span> Check the first one hundred combinations.</span>
__k <span class="Keyword">=</span> [];
<span class="Keyword">for</span> (__l<span class="Keyword">=</span><span class="Number">0</span>, i<span class="Keyword">=</span><span class="Number">1</span>, __j<span class="Keyword">=</span><span class="Number">100</span>; i<span class="Keyword">&lt;=</span>__j; i<span class="Keyword">++</span>, __l<span class="Keyword">++</span>) {
__k[__l] <span class="Keyword">=</span> lockpick(combinations[i]);
}
__k;
</pre><br class='clear' /></div> </pre><br class='clear' /></div>
<p>
If you're not iterating over an actual array, you can use a range to
specify the start and end of an array comprehension:
<tt>coundown(i) for i in [10..1].</tt>
</p>
<p id="slice"> <p id="slice">
<b class="header">Slicing Arrays with Ranges</b> <b class="header">Slicing Arrays with Ranges</b>
@@ -684,8 +678,7 @@ tom.move()
<span class="Keyword">return</span> <span class="LibraryFunction">alert</span>(<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>); <span class="Keyword">return</span> <span class="LibraryFunction">alert</span>(<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span> moved <span class="String">&quot;</span></span> <span class="Keyword">+</span> meters <span class="Keyword">+</span> <span class="String"><span class="String">&quot;</span>m.<span class="String">&quot;</span></span>);
}; };
<span class="FunctionName">Snake</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) { <span class="FunctionName">Snake</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name; <span class="Keyword">return</span> (<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name);
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>;
}; };
Snake.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryConstant">prototype</span>; Snake.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryConstant">prototype</span>;
<span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>(); <span class="LibraryClassType">Snake</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
@@ -695,8 +688,7 @@ Snake.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryC
<span class="Keyword">return</span> Snake.__superClass__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">5</span>); <span class="Keyword">return</span> Snake.__superClass__.move.<span class="LibraryFunction">call</span>(<span class="Variable">this</span>, <span class="Number">5</span>);
}; };
<span class="FunctionName">Horse</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) { <span class="FunctionName">Horse</span> = <span class="Storage">function</span>(<span class="FunctionArgument">name</span>) {
<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name; <span class="Keyword">return</span> (<span class="Variable">this</span>.<span class="LibraryConstant">name</span> <span class="Keyword">=</span> name);
<span class="Keyword">return</span> <span class="Variable">this</span>.<span class="LibraryConstant">name</span>;
}; };
Horse.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryConstant">prototype</span>; Horse.__superClass__ <span class="Keyword">=</span> Animal.<span class="LibraryConstant">prototype</span>;
<span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>(); <span class="LibraryClassType">Horse</span>.<span class="LibraryConstant">prototype</span> = <span class="Keyword">new</span> <span class="TypeName">Animal</span>();
@@ -716,8 +708,7 @@ Animal.prototype.move = function(meters) {
return alert(this.name + " moved " + meters + "m."); return alert(this.name + " moved " + meters + "m.");
}; };
Snake = function(name) { Snake = function(name) {
this.name = name; return (this.name = name);
return this.name;
}; };
Snake.__superClass__ = Animal.prototype; Snake.__superClass__ = Animal.prototype;
Snake.prototype = new Animal(); Snake.prototype = new Animal();
@@ -727,8 +718,7 @@ Snake.prototype.move = function() {
return Snake.__superClass__.move.call(this, 5); return Snake.__superClass__.move.call(this, 5);
}; };
Horse = function(name) { Horse = function(name) {
this.name = name; return (this.name = name);
return this.name;
}; };
Horse.__superClass__ = Animal.prototype; Horse.__superClass__ = Animal.prototype;
Horse.prototype = new Animal(); Horse.prototype = new Animal();
@@ -892,6 +882,22 @@ world...";
</ul> </ul>
<h2 id="change_log">Change Log</h2> <h2 id="change_log">Change Log</h2>
<p>
<b class="header" style="margin-top: 20px;">0.1.6</b>
Bugfix for running <tt>coffee --interactive</tt> and <tt>--run</tt>
from outside of the CoffeeScript directory. Bugfix for nested
function/if-statements.
</p>
<p>
<b class="header" style="margin-top: 20px;">0.1.5</b>
Array slice literals and array comprehensions can now both take Ruby-style
ranges to specify the start and end. JavaScript variable declaration is
now pushed up to the top of the scope, making all assignment statements into
expressions. You can use <tt>\</tt> to escape newlines.
The <tt>coffee-script</tt> command is now called <tt>coffee</tt>.
</p>
<p> <p>
<b class="header" style="margin-top: 20px;">0.1.4</b> <b class="header" style="margin-top: 20px;">0.1.4</b>

View File

@@ -9,7 +9,7 @@ require "coffee_script/parse_error"
# Namespace for all CoffeeScript internal classes. # Namespace for all CoffeeScript internal classes.
module CoffeeScript module CoffeeScript
VERSION = '0.1.4' # Keep in sync with the gemspec. VERSION = '0.1.6' # Keep in sync with the gemspec.
# Compile a script (String or IO) to JavaScript. # Compile a script (String or IO) to JavaScript.
def self.compile(script, options={}) def self.compile(script, options={})

View File

@@ -19,6 +19,10 @@ Usage:
# Seconds to pause between checks for changed source files. # Seconds to pause between checks for changed source files.
WATCH_INTERVAL = 0.5 WATCH_INTERVAL = 0.5
# Command to execute in Narwhal
PACKAGE = File.expand_path(File.dirname(__FILE__) + '/../..')
LAUNCHER = "narwhal -p #{PACKAGE} -e 'require(\"coffee-script\").run(system.args);'"
# Run the CommandLine off the contents of ARGV. # Run the CommandLine off the contents of ARGV.
def initialize def initialize
@mtimes = {} @mtimes = {}
@@ -104,7 +108,7 @@ Usage:
# Use Narwhal to run an interactive CoffeeScript session. # Use Narwhal to run an interactive CoffeeScript session.
def launch_repl def launch_repl
exec "narwhal lib/coffee_script/narwhal/js/launcher.js" exec "#{LAUNCHER}"
rescue Errno::ENOENT rescue Errno::ENOENT
puts "Error: Narwhal must be installed to use the interactive REPL." puts "Error: Narwhal must be installed to use the interactive REPL."
exit(1) exit(1)
@@ -113,7 +117,7 @@ Usage:
# Use Narwhal to compile and execute CoffeeScripts. # Use Narwhal to compile and execute CoffeeScripts.
def run_scripts def run_scripts
sources = @sources.join(' ') sources = @sources.join(' ')
exec "narwhal lib/coffee_script/narwhal/js/launcher.js #{sources}" exec "#{LAUNCHER} #{sources}"
rescue Errno::ENOENT rescue Errno::ENOENT
puts "Error: Narwhal must be installed in order to execute CoffeeScripts." puts "Error: Narwhal must be installed in order to execute CoffeeScripts."
exit(1) exit(1)

View File

@@ -327,7 +327,9 @@ rule
# Looks a little confusing, check nodes.rb for the arguments to ForNode. # Looks a little confusing, check nodes.rb for the arguments to ForNode.
For: For:
Expression 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][0], val[2][0], val[3][1], val[2][1]) }
| FOR ForVariables ForSource
Terminator Expressions "." { result = ForNode.new(val[4], val[2][0], val[1][0], val[2][1], val[1][1]) }
; ;
# An array comprehension has variables for the current element and index. # An array comprehension has variables for the current element and index.
@@ -338,9 +340,9 @@ rule
# The source of the array comprehension can optionally be filtered. # The source of the array comprehension can optionally be filtered.
ForSource: ForSource:
IN PureExpression "." { result = [val[1]] } IN PureExpression { result = [val[1]] }
| IN PureExpression | IN PureExpression
IF Expression "." { result = [val[1], val[3]] } IF Expression { result = [val[1], val[3]] }
; ;
# Switch/When blocks. # Switch/When blocks.

View File

@@ -19,7 +19,7 @@ module CoffeeScript
# Token matching regexes. # Token matching regexes.
IDENTIFIER = /\A([a-zA-Z$_]\w*)/ IDENTIFIER = /\A([a-zA-Z$_]\w*)/
NUMBER = /\A\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?))\b/i NUMBER = /\A((\b|-)((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
STRING = /\A(""|''|"(.*?)[^\\]"|'(.*?)[^\\]')/m STRING = /\A(""|''|"(.*?)[^\\]"|'(.*?)[^\\]')/m
JS = /\A(``|`(.*?)[^\\]`)/m JS = /\A(``|`(.*?)[^\\]`)/m
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/ OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/

View File

@@ -20,8 +20,9 @@ checkForErrors: coffeeProcess =>
# Run a simple REPL, round-tripping to the CoffeeScript compiler for every # Run a simple REPL, round-tripping to the CoffeeScript compiler for every
# command. # command.
exports.run: args => exports.run: args =>
args.shift() if args.length
return require(File.absolute(args[0])) if args.length exports.evalCS(File.read(path)) for path in args.
return true.
while true while true
try try

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

@@ -18,10 +18,16 @@
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every // Run a simple REPL, round-tripping to the CoffeeScript compiler for every
// command. // command.
exports.run = function(args) { exports.run = function(args) {
var result; var __a, __b, __c, __d, path, result;
args.shift();
if (args.length) { if (args.length) {
return require(File.absolute(args[0])); __a = args;
__d = [];
for (__b=0, __c=__a.length; __b<__c; __b++) {
path = __a[__b];
__d[__b] = exports.evalCS(File.read(path));
}
__d;
return true;
} }
while (true) { while (true) {
try { try {

View File

@@ -8,8 +8,7 @@
// Reload the coffee-script environment from source. // Reload the coffee-script environment from source.
reload: function(topId, path) { reload: function(topId, path) {
coffeescript = coffeescript || require('coffee-script'); coffeescript = coffeescript || require('coffee-script');
factories[topId] = coffeescript.makeNarwhalFactory(path); return (factories[topId] = coffeescript.makeNarwhalFactory(path));
return factories[topId];
}, },
// Ensure that the coffee-script environment is loaded. // Ensure that the coffee-script environment is loaded.
load: function(topId, path) { load: function(topId, path) {

View File

@@ -95,6 +95,7 @@ module CoffeeScript
if node.statement? || node.custom_return? if node.statement? || node.custom_return?
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}" "#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
else else
o.delete(:return)
"#{o[:indent]}return #{node.compile(o)}#{node.line_ending}" "#{o[:indent]}return #{node.compile(o)}#{node.line_ending}"
end end
elsif o[:assign] elsif o[:assign]
@@ -307,8 +308,6 @@ module CoffeeScript
# A range literal. Ranges can be used to extract portions (slices) of arrays, # A range literal. Ranges can be used to extract portions (slices) of arrays,
# or to specify a range for array comprehensions. # or to specify a range for array comprehensions.
# RangeNodes get expanded into the equivalent array, if not used to index
# a slice or define an array comprehension.
class RangeNode class RangeNode
attr_reader :from, :to attr_reader :from, :to
@@ -320,15 +319,20 @@ module CoffeeScript
@exclusive @exclusive
end end
# TODO -- figure out if we can detect if a range runs negative. def less_operator
def downward? @exclusive ? '<' : '<='
end end
# TODO -- figure out if we can expand ranges into the corresponding array, def greater_operator
# as an expression, reliably. @exclusive ? '>' : '>='
def compile(o={}) end
write()
def compile(o, fv, tv)
fvv, tvv = @from.compile(o), @to.compile(o)
vars = "#{fv}=#{fvv}, #{tv}=#{tvv}"
compare = "(#{fvv} <= #{tvv} ? #{fv} #{less_operator} #{tv} : #{fv} #{greater_operator} #{tv})"
incr = "(#{fvv} <= #{tvv} ? #{fv} += 1 : #{fv} -= 1)"
"#{vars}; #{compare}; #{incr}"
end end
end end
@@ -374,12 +378,11 @@ module CoffeeScript
last = @variable.last.to_s last = @variable.last.to_s
proto = name[PROTO_ASSIGN, 1] proto = name[PROTO_ASSIGN, 1]
o = o.merge(:assign => @variable, :last_assign => last, :proto_assign => proto) o = o.merge(:assign => @variable, :last_assign => last, :proto_assign => proto)
postfix = o[:return] ? ";\n#{o[:indent]}return #{name}" : ''
return write("#{name}: #{@value.compile(o)}") if @context == :object return write("#{name}: #{@value.compile(o)}") if @context == :object
return write("#{name} = #{@value.compile(o)}#{postfix}") if @variable.properties? && !@value.custom_assign?
o[:scope].find(name) unless @variable.properties? o[:scope].find(name) unless @variable.properties?
return write(@value.compile(o)) if @value.custom_assign? return write(@value.compile(o)) if @value.custom_assign?
write("#{name} = #{@value.compile(o)}#{postfix}") val = "#{name} = #{@value.compile(o)}"
write(o[:return] && !@value.custom_return? ? "return (#{val})" : val)
end end
end end
@@ -548,11 +551,10 @@ module CoffeeScript
index_name = @index ? @index : nil index_name = @index ? @index : nil
if range if range
source_part = '' source_part = ''
operator = @source.exclusive? ? '<' : '<='
index_var = scope.free_variable
for_part = "#{index_var}=0, #{ivar}=#{@source.from.compile(o)}, #{lvar}=#{@source.to.compile(o)}; #{ivar}#{operator}#{lvar}; #{ivar}++, #{index_var}++"
var_part = '' var_part = ''
index_part = '' index_part = ''
index_var = scope.free_variable
for_part = "#{index_var}=0, #{@source.compile(o, ivar, lvar)}, #{index_var}++"
else else
index_var = nil index_var = nil
source_part = "#{svar} = #{@source.compile(o)};\n#{o[:indent]}" source_part = "#{svar} = #{@source.compile(o)};\n#{o[:indent]}"
@@ -569,6 +571,8 @@ module CoffeeScript
if o[:return] || o[:assign] if o[:return] || o[:assign]
return_result = "#{o[:assign].compile(o)} = #{return_result}" if o[:assign] return_result = "#{o[:assign].compile(o)} = #{return_result}" if o[:assign]
return_result = "return #{return_result}" if o[:return] return_result = "return #{return_result}" if o[:return]
o.delete(:assign)
o.delete(:return)
if @filter if @filter
body = CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [@body]) body = CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [@body])
body = IfNode.new(@filter, body, nil, :statement => true) body = IfNode.new(@filter, body, nil, :statement => true)
@@ -726,8 +730,11 @@ module CoffeeScript
# force sub-else bodies into statement form. # force sub-else bodies into statement form.
def compile_statement(o) def compile_statement(o)
indent = o[:indent] indent = o[:indent]
cond_o = o.dup
cond_o.delete(:assign)
cond_o.delete(:return)
o[:indent] += TAB o[:indent] += TAB
if_part = "if (#{@condition.compile(o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}" if_part = "if (#{@condition.compile(cond_o)}) {\n#{Expressions.wrap(@body).compile(o)}\n#{indent}}"
return if_part unless @else_body return if_part unless @else_body
else_part = chain? ? else_part = chain? ?
" else #{@else_body.compile(o.merge(:indent => indent))}" : " else #{@else_body.compile(o.merge(:indent => indent))}" :

View File

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

View File

@@ -1,4 +1,8 @@
nums: n * n for n in [1, 2, 3] if n % 2 isnt 0. nums: n * n for n in [1, 2, 3] if n % 2 isnt 0.
result: n * 2 for n in nums. results: n * 2 for n in nums.
print(result.join(',') is '2,18') # next: for n in [1, 2, 3] if n % 2 isnt 0
# print('hi') if false
# n * n * 2.
print(results.join(',') is '2,18')

View File

@@ -1,5 +1,8 @@
nums: i * 3 for i in [1..3]. nums: i * 3 for i in [1..3].
result: nums.join(', ') negs: x for x in [-20..-10].
negs: negs[0..2]
print(result is '3, 6, 9') result: nums.concat(negs).join(', ')
print(result is '3, 6, 9, -20, -19, -18')