diff --git a/examples/underscore.coffee b/examples/underscore.coffee index a6c5baf6..8e2328e6 100644 --- a/examples/underscore.coffee +++ b/examples/underscore.coffee @@ -47,7 +47,7 @@ _.each: obj, iterator, context => try return obj.forEach(iterator, context) if obj.forEach if _.isArray(obj) or _.isArguments(obj) - return iterator.call(context, item, i, obj) for item, i in obj + return iterator.call(context, item, i, obj) for item, i in obj iterator.call(context, obj[key], key, obj) for key in _.keys(obj) catch e throw e if e isnt breaker @@ -58,34 +58,33 @@ _.each: obj, iterator, context => _.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) + _.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)) - reducer: value, index, list => memo: iterator.call(context, memo, value, index, list) - _.each(obj, reducer) + _.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)) - reversed: _.clone(_.toArray(obj)).reverse() - reverser: value, index => memo: iterator.call(context, memo, value, index, obj) - _.each(reversed, reverser) + _.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())) + _.breakLoop() result # Return all the elements that pass a truth test. Use JavaScript 1.6's @@ -187,72 +186,59 @@ _.max: obj, iterator, context => # } # 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; -# }); -# }; -# + +# 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 => el for el in array when el + +# 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) + _.select(array, (value => not _.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: array, isSorted => + _.reduce(array, []) memo, el, i => + if (i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))) + memo.push(el) + memo + # # Produce an array that contains every item shared between all the # # passed-in arrays. # _.intersect = function(array) { @@ -316,21 +302,18 @@ _.bind: func, obj => args: _.rest(arguments, 2) => func.apply(obj or root, args.concat(_.toArray(arguments))) -# # Bind all of an object's methods to that object. Useful for ensuring that -# # all callbacks defined on an object belong to it. -# _.bindAll = 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); -# }; +# Bind all of an object's methods to that object. Useful for ensuring that +# all callbacks defined on an object belong to it. +_.bindAll: obj => + funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj) + _.each(funcs, (f => obj[f]: _.bind(obj[f], obj))) + obj + +# Delays a function for the given number of milliseconds, and then calls +# it with the arguments supplied. +_.delay: func, wait => + args: _.rest(arguments, 2) + setTimeout((=> func.apply(func, args)), wait) # Defers a function, scheduling it to run after the current call stack has # cleared. @@ -352,37 +335,30 @@ _.compose: => args: [funcs[i]].apply(this, args) for i in [(funcs.length - 1)..0] args[0] -# /* ------------------------- 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); -# }; +# ------------------------- 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 => + destination[key]: val for val, key in source + destination + +# Create a (shallow-cloned) duplicate of an object. +_.clone: obj => + return obj.slice(0) if _.isArray(ob) + _.extend({}, obj) # Perform a deep comparison to check if two objects are equal. _.isEqual: a, b => @@ -444,16 +420,6 @@ _.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 + if chain then _(obj).chain() else obj + # # # Add all of the Underscore functions to the wrapper object. # _.each(_.functions(_), function(name) { @@ -530,24 +496,17 @@ _.methods: _.functions # 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; -# }; -# -# + +# 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 diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 83ace2e3..f1c280bf 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -8,8 +8,8 @@ token IDENTIFIER PROPERTY_ACCESS token CODE PARAM PARAM_SPLAT NEW RETURN token TRY CATCH FINALLY THROW token BREAK CONTINUE -token FOR IN BY WHILE -token SWITCH WHEN +token FOR IN BY WHEN WHILE +token SWITCH LEADING_WHEN token DELETE INSTANCEOF TYPEOF token SUPER EXTENDS token NEWLINE @@ -32,7 +32,7 @@ prechigh left '.' right INDENT left OUTDENT - right WHEN IN BY + right WHEN LEADING_WHEN IN BY right THROW FOR NEW SUPER left EXTENDS left ASSIGN '||=' '&&=' @@ -367,8 +367,9 @@ rule # An individual when. When: - WHEN Expression Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) } - | WHEN Expression Block Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) } + 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 ; diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 706b1d51..d357264f 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -85,6 +85,7 @@ module CoffeeScript # 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 diff --git a/lib/coffee_script/rewriter.rb b/lib/coffee_script/rewriter.rb index b0319623..88a31066 100644 --- a/lib/coffee_script/rewriter.rb +++ b/lib/coffee_script/rewriter.rb @@ -27,7 +27,7 @@ module CoffeeScript # Single-line flavors of block expressions that have unclosed endings. # The grammar can't disambiguate them, so we insert the implicit indentation. SINGLE_LINERS = [:ELSE, "=>", :TRY, :FINALLY, :THEN] - SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE, :OUTDENT, :WHEN] + 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