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
This commit is contained in:
parent
32cd15f038
commit
672dd70bdb
|
@ -58,34 +58,33 @@ _.each: obj, iterator, context =>
|
||||||
_.map: obj, iterator, context =>
|
_.map: obj, iterator, context =>
|
||||||
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
|
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
|
||||||
results: []
|
results: []
|
||||||
mapper: value, index, list => results.push(iterator.call(context, value, index, list))
|
_.each(obj) value, index, list =>
|
||||||
_.each(obj, mapper)
|
results.push(iterator.call(context, value, index, list))
|
||||||
results
|
results
|
||||||
|
|
||||||
# Reduce builds up a single result from a list of values. Also known as
|
# 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.
|
# inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
|
||||||
_.reduce: obj, memo, iterator, context =>
|
_.reduce: obj, memo, iterator, context =>
|
||||||
return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
|
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) value, index, list =>
|
||||||
_.each(obj, reducer)
|
memo: iterator.call(context, memo, value, index, list)
|
||||||
memo
|
memo
|
||||||
|
|
||||||
# The right-associative version of reduce, also known as foldr. Uses
|
# The right-associative version of reduce, also known as foldr. Uses
|
||||||
# JavaScript 1.8's version of reduceRight, if available.
|
# JavaScript 1.8's version of reduceRight, if available.
|
||||||
_.reduceRight: obj, memo, iterator, context =>
|
_.reduceRight: obj, memo, iterator, context =>
|
||||||
return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
|
return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
|
||||||
reversed: _.clone(_.toArray(obj)).reverse()
|
_.each(_.clone(_.toArray(obj)).reverse()) value, index =>
|
||||||
reverser: value, index => memo: iterator.call(context, memo, value, index, obj)
|
memo: iterator.call(context, memo, value, index, obj)
|
||||||
_.each(reversed, reverser)
|
|
||||||
memo
|
memo
|
||||||
|
|
||||||
# Return the first value which passes a truth test.
|
# Return the first value which passes a truth test.
|
||||||
_.detect: obj, iterator, context =>
|
_.detect: obj, iterator, context =>
|
||||||
result: null
|
result: null
|
||||||
_.each(obj, (value, index, list =>
|
_.each(obj) value, index, list =>
|
||||||
if iterator.call(context, value, index, list)
|
if iterator.call(context, value, index, list)
|
||||||
result: value
|
result: value
|
||||||
_.breakLoop()))
|
_.breakLoop()
|
||||||
result
|
result
|
||||||
|
|
||||||
# Return all the elements that pass a truth test. Use JavaScript 1.6's
|
# Return all the elements that pass a truth test. Use JavaScript 1.6's
|
||||||
|
@ -187,72 +186,59 @@ _.max: obj, iterator, context =>
|
||||||
# }
|
# }
|
||||||
# return low;
|
# return low;
|
||||||
# };
|
# };
|
||||||
#
|
|
||||||
# # Convert anything iterable into a real, live array.
|
# Convert anything iterable into a real, live array.
|
||||||
# _.toArray = function(iterable) {
|
_.toArray: iterable =>
|
||||||
# if (!iterable) return [];
|
return [] if (!iterable)
|
||||||
# if (iterable.toArray) return iterable.toArray();
|
return iterable.toArray() if (iterable.toArray)
|
||||||
# if (_.isArray(iterable)) return iterable;
|
return iterable if (_.isArray(iterable))
|
||||||
# if (_.isArguments(iterable)) return slice.call(iterable);
|
return slice.call(iterable) if (_.isArguments(iterable))
|
||||||
# return _.map(iterable, function(val){ return val; });
|
_.values(iterable)
|
||||||
# };
|
|
||||||
#
|
# Return the number of elements in an object.
|
||||||
# # Return the number of elements in an object.
|
_.size: obj => _.toArray(obj).length
|
||||||
# _.size = function(obj) {
|
|
||||||
# return _.toArray(obj).length;
|
# -------------------------- Array Functions: ------------------------------
|
||||||
# };
|
|
||||||
#
|
# Get the first element of an array. Passing "n" will return the first N
|
||||||
# /*-------------------------- Array Functions: ------------------------------*/
|
# values in the array. Aliased as "head". The "guard" check allows it to work
|
||||||
#
|
# with _.map.
|
||||||
# # Get the first element of an array. Passing "n" will return the first N
|
_.first: array, n, guard =>
|
||||||
# # values in the array. Aliased as "head". The "guard" check allows it to work
|
if n and not guard then slice.call(array, 0, n) else array[0]
|
||||||
# # with _.map.
|
|
||||||
# _.first = function(array, n, guard) {
|
# Returns everything but the first entry of the array. Aliased as "tail".
|
||||||
# return n && !guard ? slice.call(array, 0, n) : array[0];
|
# 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.
|
||||||
# # Returns everything but the first entry of the array. Aliased as "tail".
|
_.rest: array, index, guard =>
|
||||||
# # Especially useful on the arguments object. Passing an "index" will return
|
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
|
||||||
# # the rest of the values in the array from that index onward. The "guard"
|
|
||||||
# //check allows it to work with _.map.
|
# Get the last element of an array.
|
||||||
# _.rest = function(array, index, guard) {
|
_.last: array => array[array.length - 1]
|
||||||
# return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
|
|
||||||
# };
|
# Trim out all falsy values from an array.
|
||||||
#
|
_.compact: array => el for el in array when el
|
||||||
# # Get the last element of an array.
|
|
||||||
# _.last = function(array) {
|
# Return a completely flattened version of an array.
|
||||||
# return array[array.length - 1];
|
_.flatten: array =>
|
||||||
# };
|
_.reduce(array, []) memo, value =>
|
||||||
#
|
return memo.concat(_.flatten(value)) if _.isArray(value)
|
||||||
# # Trim out all falsy values from an array.
|
memo.push(value)
|
||||||
# _.compact = function(array) {
|
memo
|
||||||
# return _.select(array, function(value){ return !!value; });
|
|
||||||
# };
|
# Return a version of the array that does not contain the specified value(s).
|
||||||
#
|
_.without: array =>
|
||||||
# # Return a completely flattened version of an array.
|
values: _.rest(arguments)
|
||||||
# _.flatten = function(array) {
|
_.select(array, (value => not _.include(values, value)))
|
||||||
# return _.reduce(array, [], function(memo, value) {
|
|
||||||
# if (_.isArray(value)) return memo.concat(_.flatten(value));
|
# Produce a duplicate-free version of the array. If the array has already
|
||||||
# memo.push(value);
|
# been sorted, you have the option of using a faster algorithm.
|
||||||
# return memo;
|
_.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)
|
||||||
# # Return a version of the array that does not contain the specified value(s).
|
memo
|
||||||
# _.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
|
# # Produce an array that contains every item shared between all the
|
||||||
# # passed-in arrays.
|
# # passed-in arrays.
|
||||||
# _.intersect = function(array) {
|
# _.intersect = function(array) {
|
||||||
|
@ -316,21 +302,18 @@ _.bind: func, obj =>
|
||||||
args: _.rest(arguments, 2)
|
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
|
# 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: obj =>
|
||||||
# var funcs = _.rest(arguments);
|
funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
|
||||||
# if (funcs.length == 0) funcs = _.functions(obj);
|
_.each(funcs, (f => obj[f]: _.bind(obj[f], obj)))
|
||||||
# _.each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
obj
|
||||||
# return obj;
|
|
||||||
# };
|
# Delays a function for the given number of milliseconds, and then calls
|
||||||
#
|
# it with the arguments supplied.
|
||||||
# # Delays a function for the given number of milliseconds, and then calls
|
_.delay: func, wait =>
|
||||||
# # it with the arguments supplied.
|
args: _.rest(arguments, 2)
|
||||||
# _.delay = function(func, wait) {
|
setTimeout((=> func.apply(func, args)), 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
|
# Defers a function, scheduling it to run after the current call stack has
|
||||||
# cleared.
|
# cleared.
|
||||||
|
@ -352,37 +335,30 @@ _.compose: =>
|
||||||
args: [funcs[i]].apply(this, args) for i in [(funcs.length - 1)..0]
|
args: [funcs[i]].apply(this, args) for i in [(funcs.length - 1)..0]
|
||||||
args[0]
|
args[0]
|
||||||
|
|
||||||
# /* ------------------------- Object Functions: ---------------------------- */
|
# ------------------------- Object Functions: ----------------------------
|
||||||
#
|
|
||||||
# # Retrieve the names of an object's properties.
|
# Retrieve the names of an object's properties.
|
||||||
# _.keys = function(obj) {
|
_.keys: obj =>
|
||||||
# if(_.isArray(obj)) return _.range(0, obj.length);
|
return _.range(0, obj.length) if _.isArray(obj)
|
||||||
# var keys = [];
|
key for val, key in obj
|
||||||
# for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
|
|
||||||
# return keys;
|
# Retrieve the values of an object's properties.
|
||||||
# };
|
_.values: obj =>
|
||||||
#
|
_.map(obj, _.identity)
|
||||||
# # Retrieve the values of an object's properties.
|
|
||||||
# _.values = function(obj) {
|
# Return a sorted list of the function names available in Underscore.
|
||||||
# return _.map(obj, _.identity);
|
_.functions: obj =>
|
||||||
# };
|
_.select(_.keys(obj), key => _.isFunction(obj[key])).sort()
|
||||||
#
|
|
||||||
# # Return a sorted list of the function names available in Underscore.
|
# Extend a given object with all of the properties in a source object.
|
||||||
# _.functions = function(obj) {
|
_.extend: destination, source =>
|
||||||
# return _.select(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
|
destination[key]: val for val, key in source
|
||||||
# };
|
destination
|
||||||
#
|
|
||||||
# # Extend a given object with all of the properties in a source object.
|
# Create a (shallow-cloned) duplicate of an object.
|
||||||
# _.extend = function(destination, source) {
|
_.clone: obj =>
|
||||||
# for (var property in source) destination[property] = source[property];
|
return obj.slice(0) if _.isArray(ob)
|
||||||
# return destination;
|
_.extend({}, obj)
|
||||||
# };
|
|
||||||
#
|
|
||||||
# # 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.
|
# Perform a deep comparison to check if two objects are equal.
|
||||||
_.isEqual: a, b =>
|
_.isEqual: a, b =>
|
||||||
|
@ -444,16 +420,6 @@ _.tap: obj, interceptor =>
|
||||||
interceptor(obj)
|
interceptor(obj)
|
||||||
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: --------------------------
|
# -------------------------- Utility Functions: --------------------------
|
||||||
|
|
||||||
# Run Underscore.js in noConflict mode, returning the '_' variable to its
|
# Run Underscore.js in noConflict mode, returning the '_' variable to its
|
||||||
|
@ -507,11 +473,11 @@ _.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.
|
||||||
# var result = function(obj, chain) {
|
result: obj, chain =>
|
||||||
# return chain ? _(obj).chain() : obj;
|
if chain then _(obj).chain() else obj
|
||||||
# };
|
|
||||||
#
|
#
|
||||||
# # Add all of the Underscore functions to the wrapper object.
|
# # Add all of the Underscore functions to the wrapper object.
|
||||||
# _.each(_.functions(_), function(name) {
|
# _.each(_.functions(_), function(name) {
|
||||||
|
@ -530,24 +496,17 @@ _.methods: _.functions
|
||||||
# return result(this._wrapped, this._chain);
|
# return result(this._wrapped, this._chain);
|
||||||
# };
|
# };
|
||||||
# });
|
# });
|
||||||
#
|
|
||||||
# # Add all accessor Array functions to the wrapper.
|
# Add all accessor Array functions to the wrapper.
|
||||||
# _.each(['concat', 'join', 'slice'], function(name) {
|
_.each(['concat', 'join', 'slice']) name =>
|
||||||
# var method = Array.prototype[name];
|
method: Array.prototype[name]
|
||||||
# wrapper.prototype[name] = function() {
|
wrapper.prototype[name]: =>
|
||||||
# return result(method.apply(this._wrapped, arguments), this._chain);
|
result(method.apply(this._wrapped, arguments), this._chain)
|
||||||
# };
|
|
||||||
# });
|
# Start chaining a wrapped Underscore object.
|
||||||
#
|
wrapper.prototype.chain: =>
|
||||||
# # Start chaining a wrapped Underscore object.
|
this._chain: true
|
||||||
# wrapper.prototype.chain = function() {
|
this
|
||||||
# this._chain = true;
|
|
||||||
# return this;
|
# Extracts the result from a wrapped and chained object.
|
||||||
# };
|
wrapper.prototype.value: => this._wrapped
|
||||||
#
|
|
||||||
# # Extracts the result from a wrapped and chained object.
|
|
||||||
# wrapper.prototype.value = function() {
|
|
||||||
# return this._wrapped;
|
|
||||||
# };
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ token IDENTIFIER PROPERTY_ACCESS
|
||||||
token CODE PARAM PARAM_SPLAT NEW RETURN
|
token CODE PARAM PARAM_SPLAT NEW RETURN
|
||||||
token TRY CATCH FINALLY THROW
|
token TRY CATCH FINALLY THROW
|
||||||
token BREAK CONTINUE
|
token BREAK CONTINUE
|
||||||
token FOR IN BY WHILE
|
token FOR IN BY WHEN WHILE
|
||||||
token SWITCH WHEN
|
token SWITCH LEADING_WHEN
|
||||||
token DELETE INSTANCEOF TYPEOF
|
token DELETE INSTANCEOF TYPEOF
|
||||||
token SUPER EXTENDS
|
token SUPER EXTENDS
|
||||||
token NEWLINE
|
token NEWLINE
|
||||||
|
@ -32,7 +32,7 @@ prechigh
|
||||||
left '.'
|
left '.'
|
||||||
right INDENT
|
right INDENT
|
||||||
left OUTDENT
|
left OUTDENT
|
||||||
right WHEN IN BY
|
right WHEN LEADING_WHEN IN BY
|
||||||
right THROW FOR NEW SUPER
|
right THROW FOR NEW SUPER
|
||||||
left EXTENDS
|
left EXTENDS
|
||||||
left ASSIGN '||=' '&&='
|
left ASSIGN '||=' '&&='
|
||||||
|
@ -367,8 +367,9 @@ rule
|
||||||
|
|
||||||
# An individual when.
|
# An individual when.
|
||||||
When:
|
When:
|
||||||
WHEN Expression Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
|
LEADING_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
|
||||||
|
Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
|
||||||
| Comment
|
| Comment
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ module CoffeeScript
|
||||||
# Keywords are special identifiers tagged with their own name,
|
# Keywords are special identifiers tagged with their own name,
|
||||||
# 'if' will result in an [:IF, "if"] token.
|
# 'if' will result in an [:IF, "if"] token.
|
||||||
tag = KEYWORDS.include?(identifier) ? identifier.upcase.to_sym : :IDENTIFIER
|
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] == '.')
|
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2][1] == '.')
|
||||||
token(tag, identifier)
|
token(tag, identifier)
|
||||||
@i += identifier.length
|
@i += identifier.length
|
||||||
|
|
|
@ -27,7 +27,7 @@ module CoffeeScript
|
||||||
# Single-line flavors of block expressions that have unclosed endings.
|
# Single-line flavors of block expressions that have unclosed endings.
|
||||||
# The grammar can't disambiguate them, so we insert the implicit indentation.
|
# The grammar can't disambiguate them, so we insert the implicit indentation.
|
||||||
SINGLE_LINERS = [:ELSE, "=>", :TRY, :FINALLY, :THEN]
|
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
|
# Rewrite the token stream in multiple passes, one logical filter at
|
||||||
# a time. This could certainly be changed into a single pass through the
|
# a time. This could certainly be changed into a single pass through the
|
||||||
|
|
Loading…
Reference in New Issue