From 94bc7c1f92101f86447a3e5d85d3ee0a88d70653 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 4 Jan 2010 01:06:31 -0500 Subject: [PATCH] putting in a special check for returns within array comprehensions -- not very nice --- examples/underscore.coffee | 18 +++++----- lib/coffee_script/nodes.rb | 67 +++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/examples/underscore.coffee b/examples/underscore.coffee index 1953dc45..4a39758f 100644 --- a/examples/underscore.coffee +++ b/examples/underscore.coffee @@ -49,7 +49,7 @@ _.each: obj, iterator, context => try return obj.forEach(iterator, context) if obj.forEach if _.isArray(obj) or _.isArguments(obj) - return (iterator.call(context, obj[i], i, obj) for i in [0...obj.length]) + return iterator.call(context, obj[i], i, obj) for i in [0...obj.length] iterator.call(context, val, key, obj) for val, key in obj catch e throw e if e isnt breaker @@ -129,10 +129,9 @@ _.any: obj, iterator, context => # based on '==='. _.include: obj, target => return _.indexOf(obj, target) isnt -1 if _.isArray(obj) - found: false - _.each(obj) value => - _.breakLoop() if found: value is target - found + for val in obj + return true if val is target + false # Invoke a method with arguments on every item in a collection. _.invoke: obj, method => @@ -247,7 +246,8 @@ _.zip: => args: _.toArray(arguments) length: _.max(_.pluck(args, 'length')) results: new Array(length) - (results[i]: _.pluck(args, String(i))) for i in [0...length] + for i in [0...length] + results[i]: _.pluck(args, String(i)) results # If the browser doesn't supply us with indexOf (I'm looking at you, MSIE), @@ -326,7 +326,8 @@ _.compose: => funcs: _.toArray(arguments) => args: _.toArray(arguments) - (args: [funcs[i].apply(this, args)]) for i in [(funcs.length - 1)..0] + for i in [(funcs.length - 1)..0] + args: [funcs[i].apply(this, args)] args[0] # ------------------------- Object Functions: ---------------------------- @@ -346,7 +347,8 @@ _.functions: obj => # Extend a given object with all of the properties in a source object. _.extend: destination, source => - (destination[key]: val) for val, key in source + for val, key in source + destination[key]: val destination # Create a (shallow-cloned) duplicate of an object. diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index c4a4fda3..4e01f8a2 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -18,6 +18,10 @@ module CoffeeScript class_eval "def statement_only?; true; end" end + def self.no_return + class_eval "def returns?; false; end" + end + def write(code) puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE'] code @@ -40,6 +44,10 @@ module CoffeeScript "(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()" end + def returns? + children.any? {|node| node && node.returns? } + end + # Default implementations of the common node methods. def unwrap; self; end def statement?; false; end @@ -50,6 +58,7 @@ module CoffeeScript class Expressions < Node statement attr_reader :expressions + alias_method :children, :expressions STRIP_TRAILING_WHITESPACE = /\s+$/ @@ -132,6 +141,7 @@ module CoffeeScript # Literals are static values that have a Ruby representation, eg.: a string, a number, # true, false, nil, etc. class LiteralNode < Node + no_return STATEMENTS = ['break', 'continue'] attr_reader :value @@ -162,6 +172,10 @@ module CoffeeScript @expression = expression end + def returns? + true + end + def compile_node(o) return write(@expression.compile(o.merge(:return => true))) if @expression.statement? compiled = @expression.compile(o) @@ -173,6 +187,7 @@ module CoffeeScript # same position. class CommentNode < Node statement_only + no_return def initialize(lines) @lines = lines.value @@ -189,6 +204,7 @@ module CoffeeScript # Node for a function invocation. Takes care of converting super() calls into # calls against the prototype's function of the same name. class CallNode < Node + no_return attr_reader :variable, :arguments def initialize(variable, arguments=[]) @@ -245,6 +261,7 @@ module CoffeeScript # After goog.inherits from the Closure Library. class ExtendsNode < Node statement + no_return attr_reader :sub_object, :super_object def initialize(sub_object, super_object) @@ -262,6 +279,7 @@ module CoffeeScript # A value, indexed or dotted into, or vanilla. class ValueNode < Node + no_return attr_reader :literal, :properties, :last, :source def initialize(literal, properties=[]) @@ -295,6 +313,7 @@ module CoffeeScript # A dotted accessor into a part of a value. class AccessorNode < Node + no_return attr_reader :name def initialize(name) @@ -308,6 +327,7 @@ module CoffeeScript # An indexed accessor into a part of an array or object. class IndexNode < Node + no_return attr_reader :index def initialize(index) @@ -322,6 +342,7 @@ module CoffeeScript # A range literal. Ranges can be used to extract portions (slices) of arrays, # or to specify a range for array comprehensions. class RangeNode < Node + no_return attr_reader :from, :to def initialize(from, to, exclusive=false) @@ -363,6 +384,7 @@ module CoffeeScript # specifies the index of the end of the slice (just like the first parameter) # is the index of the beginning. class SliceNode < Node + no_return attr_reader :range def initialize(range) @@ -388,6 +410,10 @@ module CoffeeScript @variable, @value, @context = variable, value, context end + def children + [@value] + end + def compile_node(o) return compile_splice(o) if @variable.properties.last.is_a?(SliceNode) name = @variable.compile(o) @@ -433,6 +459,10 @@ module CoffeeScript @operator = CONVERSIONS[operator.to_sym] || operator end + def children + [@first, @second] + end + def unary? @second.nil? end @@ -459,6 +489,7 @@ module CoffeeScript # A function definition. The only node that creates a new Scope. class CodeNode < Node + no_return attr_reader :params, :body def initialize(params, body) @@ -489,6 +520,7 @@ module CoffeeScript # A parameter splat in a function definition. class ParamSplatNode < Node + no_return attr_accessor :index attr_reader :name @@ -503,6 +535,7 @@ module CoffeeScript end class ArgSplatNode < Node + no_return attr_reader :value def initialize(value) @@ -517,6 +550,7 @@ module CoffeeScript # An object literal. class ObjectNode < Node + no_return attr_reader :properties def initialize(properties = []) @@ -544,6 +578,7 @@ module CoffeeScript # An array literal. class ArrayNode < Node + no_return attr_reader :objects def initialize(objects=[]) @@ -574,6 +609,10 @@ module CoffeeScript @condition, @body = condition, body end + def children + [@body] + end + def compile_node(o) returns = o.delete(:return) indent = o[:indent] @@ -601,6 +640,10 @@ module CoffeeScript @step = source[:step] end + def children + [@body] + end + def compile_node(o) range = @source.is_a?(RangeNode) scope = o[:scope] @@ -629,10 +672,12 @@ module CoffeeScript set_result = "#{rvar} = [];\n#{o[:indent]}" return_result = rvar temp_var = ValueNode.new(LiteralNode.new(tvar)) - body = Expressions.wrap( - AssignNode.new(temp_var, @body.unwrap), - CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [temp_var]) - ) + unless @body.returns? + body = Expressions.wrap( + AssignNode.new(temp_var, @body.unwrap), + CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [temp_var]) + ) + end if o[:return] return_result = "return #{return_result}" if o[:return] o.delete(:return) @@ -657,6 +702,10 @@ module CoffeeScript @try, @error, @recovery, @finally = try, error, recovery, finally end + def children + [@try, @recovery, @finally] + end + def compile_node(o) indent = o[:indent] o[:indent] += TAB @@ -670,6 +719,7 @@ module CoffeeScript # Throw an exception. class ThrowNode < Node + no_return statement_only attr_reader :expression @@ -685,6 +735,7 @@ module CoffeeScript # Check an expression for existence (meaning not null or undefined). class ExistenceNode < Node + no_return attr_reader :expression def initialize(expression) @@ -708,6 +759,10 @@ module CoffeeScript @line = line end + def children + [@expressions] + end + def compile_node(o) compiled = @expressions.compile(o) compiled = compiled[0...-1] if compiled[-1..-1] == ';' @@ -730,6 +785,10 @@ module CoffeeScript @condition = OpNode.new("!", ParentheticalNode.new(@condition)) if @tags[:invert] end + def children + [@body, @else_body] + end + def <<(else_body) eb = else_body.unwrap @else_body ? @else_body << eb : @else_body = eb