putting in a special check for returns within array comprehensions -- not very nice

This commit is contained in:
Jeremy Ashkenas
2010-01-04 01:06:31 -05:00
parent f75d98e447
commit 94bc7c1f92
2 changed files with 73 additions and 12 deletions

View File

@@ -49,7 +49,7 @@ _.each: obj, iterator, context =>
try try
return obj.forEach(iterator, context) if obj.forEach return obj.forEach(iterator, context) if obj.forEach
if _.isArray(obj) or _.isArguments(obj) 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 iterator.call(context, val, key, obj) for val, key in obj
catch e catch e
throw e if e isnt breaker throw e if e isnt breaker
@@ -129,10 +129,9 @@ _.any: obj, iterator, context =>
# based on '==='. # based on '==='.
_.include: obj, target => _.include: obj, target =>
return _.indexOf(obj, target) isnt -1 if _.isArray(obj) return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
found: false for val in obj
_.each(obj) value => return true if val is target
_.breakLoop() if found: value is target false
found
# Invoke a method with arguments on every item in a collection. # Invoke a method with arguments on every item in a collection.
_.invoke: obj, method => _.invoke: obj, method =>
@@ -247,7 +246,8 @@ _.zip: =>
args: _.toArray(arguments) args: _.toArray(arguments)
length: _.max(_.pluck(args, 'length')) length: _.max(_.pluck(args, 'length'))
results: new Array(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 results
# If the browser doesn't supply us with indexOf (I'm looking at you, MSIE), # If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
@@ -326,7 +326,8 @@ _.compose: =>
funcs: _.toArray(arguments) funcs: _.toArray(arguments)
=> =>
args: _.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] args[0]
# ------------------------- Object Functions: ---------------------------- # ------------------------- Object Functions: ----------------------------
@@ -346,7 +347,8 @@ _.functions: obj =>
# Extend a given object with all of the properties in a source object. # Extend a given object with all of the properties in a source object.
_.extend: destination, source => _.extend: destination, source =>
(destination[key]: val) for val, key in source for val, key in source
destination[key]: val
destination destination
# Create a (shallow-cloned) duplicate of an object. # Create a (shallow-cloned) duplicate of an object.

View File

@@ -18,6 +18,10 @@ module CoffeeScript
class_eval "def statement_only?; true; end" class_eval "def statement_only?; true; end"
end end
def self.no_return
class_eval "def returns?; false; end"
end
def write(code) def write(code)
puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE'] puts "#{self.class.to_s}:\n#{@options.inspect}\n#{code}\n\n" if ENV['VERBOSE']
code code
@@ -40,6 +44,10 @@ module CoffeeScript
"(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()" "(function() {\n#{compile_node(o.merge(:return => true))}\n#{indent}})()"
end end
def returns?
children.any? {|node| node && node.returns? }
end
# Default implementations of the common node methods. # Default implementations of the common node methods.
def unwrap; self; end def unwrap; self; end
def statement?; false; end def statement?; false; end
@@ -50,6 +58,7 @@ module CoffeeScript
class Expressions < Node class Expressions < Node
statement statement
attr_reader :expressions attr_reader :expressions
alias_method :children, :expressions
STRIP_TRAILING_WHITESPACE = /\s+$/ STRIP_TRAILING_WHITESPACE = /\s+$/
@@ -132,6 +141,7 @@ module CoffeeScript
# Literals are static values that have a Ruby representation, eg.: a string, a number, # Literals are static values that have a Ruby representation, eg.: a string, a number,
# true, false, nil, etc. # true, false, nil, etc.
class LiteralNode < Node class LiteralNode < Node
no_return
STATEMENTS = ['break', 'continue'] STATEMENTS = ['break', 'continue']
attr_reader :value attr_reader :value
@@ -162,6 +172,10 @@ module CoffeeScript
@expression = expression @expression = expression
end end
def returns?
true
end
def compile_node(o) def compile_node(o)
return write(@expression.compile(o.merge(:return => true))) if @expression.statement? return write(@expression.compile(o.merge(:return => true))) if @expression.statement?
compiled = @expression.compile(o) compiled = @expression.compile(o)
@@ -173,6 +187,7 @@ module CoffeeScript
# same position. # same position.
class CommentNode < Node class CommentNode < Node
statement_only statement_only
no_return
def initialize(lines) def initialize(lines)
@lines = lines.value @lines = lines.value
@@ -189,6 +204,7 @@ module CoffeeScript
# Node for a function invocation. Takes care of converting super() calls into # Node for a function invocation. Takes care of converting super() calls into
# calls against the prototype's function of the same name. # calls against the prototype's function of the same name.
class CallNode < Node class CallNode < Node
no_return
attr_reader :variable, :arguments attr_reader :variable, :arguments
def initialize(variable, arguments=[]) def initialize(variable, arguments=[])
@@ -245,6 +261,7 @@ module CoffeeScript
# After goog.inherits from the Closure Library. # After goog.inherits from the Closure Library.
class ExtendsNode < Node class ExtendsNode < Node
statement statement
no_return
attr_reader :sub_object, :super_object attr_reader :sub_object, :super_object
def initialize(sub_object, super_object) def initialize(sub_object, super_object)
@@ -262,6 +279,7 @@ module CoffeeScript
# A value, indexed or dotted into, or vanilla. # A value, indexed or dotted into, or vanilla.
class ValueNode < Node class ValueNode < Node
no_return
attr_reader :literal, :properties, :last, :source attr_reader :literal, :properties, :last, :source
def initialize(literal, properties=[]) def initialize(literal, properties=[])
@@ -295,6 +313,7 @@ module CoffeeScript
# A dotted accessor into a part of a value. # A dotted accessor into a part of a value.
class AccessorNode < Node class AccessorNode < Node
no_return
attr_reader :name attr_reader :name
def initialize(name) def initialize(name)
@@ -308,6 +327,7 @@ module CoffeeScript
# An indexed accessor into a part of an array or object. # An indexed accessor into a part of an array or object.
class IndexNode < Node class IndexNode < Node
no_return
attr_reader :index attr_reader :index
def initialize(index) def initialize(index)
@@ -322,6 +342,7 @@ 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.
class RangeNode < Node class RangeNode < Node
no_return
attr_reader :from, :to attr_reader :from, :to
def initialize(from, to, exclusive=false) 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) # specifies the index of the end of the slice (just like the first parameter)
# is the index of the beginning. # is the index of the beginning.
class SliceNode < Node class SliceNode < Node
no_return
attr_reader :range attr_reader :range
def initialize(range) def initialize(range)
@@ -388,6 +410,10 @@ module CoffeeScript
@variable, @value, @context = variable, value, context @variable, @value, @context = variable, value, context
end end
def children
[@value]
end
def compile_node(o) def compile_node(o)
return compile_splice(o) if @variable.properties.last.is_a?(SliceNode) return compile_splice(o) if @variable.properties.last.is_a?(SliceNode)
name = @variable.compile(o) name = @variable.compile(o)
@@ -433,6 +459,10 @@ module CoffeeScript
@operator = CONVERSIONS[operator.to_sym] || operator @operator = CONVERSIONS[operator.to_sym] || operator
end end
def children
[@first, @second]
end
def unary? def unary?
@second.nil? @second.nil?
end end
@@ -459,6 +489,7 @@ module CoffeeScript
# A function definition. The only node that creates a new Scope. # A function definition. The only node that creates a new Scope.
class CodeNode < Node class CodeNode < Node
no_return
attr_reader :params, :body attr_reader :params, :body
def initialize(params, body) def initialize(params, body)
@@ -489,6 +520,7 @@ module CoffeeScript
# A parameter splat in a function definition. # A parameter splat in a function definition.
class ParamSplatNode < Node class ParamSplatNode < Node
no_return
attr_accessor :index attr_accessor :index
attr_reader :name attr_reader :name
@@ -503,6 +535,7 @@ module CoffeeScript
end end
class ArgSplatNode < Node class ArgSplatNode < Node
no_return
attr_reader :value attr_reader :value
def initialize(value) def initialize(value)
@@ -517,6 +550,7 @@ module CoffeeScript
# An object literal. # An object literal.
class ObjectNode < Node class ObjectNode < Node
no_return
attr_reader :properties attr_reader :properties
def initialize(properties = []) def initialize(properties = [])
@@ -544,6 +578,7 @@ module CoffeeScript
# An array literal. # An array literal.
class ArrayNode < Node class ArrayNode < Node
no_return
attr_reader :objects attr_reader :objects
def initialize(objects=[]) def initialize(objects=[])
@@ -574,6 +609,10 @@ module CoffeeScript
@condition, @body = condition, body @condition, @body = condition, body
end end
def children
[@body]
end
def compile_node(o) def compile_node(o)
returns = o.delete(:return) returns = o.delete(:return)
indent = o[:indent] indent = o[:indent]
@@ -601,6 +640,10 @@ module CoffeeScript
@step = source[:step] @step = source[:step]
end end
def children
[@body]
end
def compile_node(o) def compile_node(o)
range = @source.is_a?(RangeNode) range = @source.is_a?(RangeNode)
scope = o[:scope] scope = o[:scope]
@@ -629,10 +672,12 @@ module CoffeeScript
set_result = "#{rvar} = [];\n#{o[:indent]}" set_result = "#{rvar} = [];\n#{o[:indent]}"
return_result = rvar return_result = rvar
temp_var = ValueNode.new(LiteralNode.new(tvar)) temp_var = ValueNode.new(LiteralNode.new(tvar))
body = Expressions.wrap( unless @body.returns?
AssignNode.new(temp_var, @body.unwrap), body = Expressions.wrap(
CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [temp_var]) AssignNode.new(temp_var, @body.unwrap),
) CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [temp_var])
)
end
if o[:return] if o[:return]
return_result = "return #{return_result}" if o[:return] return_result = "return #{return_result}" if o[:return]
o.delete(:return) o.delete(:return)
@@ -657,6 +702,10 @@ module CoffeeScript
@try, @error, @recovery, @finally = try, error, recovery, finally @try, @error, @recovery, @finally = try, error, recovery, finally
end end
def children
[@try, @recovery, @finally]
end
def compile_node(o) def compile_node(o)
indent = o[:indent] indent = o[:indent]
o[:indent] += TAB o[:indent] += TAB
@@ -670,6 +719,7 @@ module CoffeeScript
# Throw an exception. # Throw an exception.
class ThrowNode < Node class ThrowNode < Node
no_return
statement_only statement_only
attr_reader :expression attr_reader :expression
@@ -685,6 +735,7 @@ module CoffeeScript
# Check an expression for existence (meaning not null or undefined). # Check an expression for existence (meaning not null or undefined).
class ExistenceNode < Node class ExistenceNode < Node
no_return
attr_reader :expression attr_reader :expression
def initialize(expression) def initialize(expression)
@@ -708,6 +759,10 @@ module CoffeeScript
@line = line @line = line
end end
def children
[@expressions]
end
def compile_node(o) def compile_node(o)
compiled = @expressions.compile(o) compiled = @expressions.compile(o)
compiled = compiled[0...-1] if compiled[-1..-1] == ';' compiled = compiled[0...-1] if compiled[-1..-1] == ';'
@@ -730,6 +785,10 @@ module CoffeeScript
@condition = OpNode.new("!", ParentheticalNode.new(@condition)) if @tags[:invert] @condition = OpNode.new("!", ParentheticalNode.new(@condition)) if @tags[:invert]
end end
def children
[@body, @else_body]
end
def <<(else_body) def <<(else_body)
eb = else_body.unwrap eb = else_body.unwrap
@else_body ? @else_body << eb : @else_body = eb @else_body ? @else_body << eb : @else_body = eb