From 9cb05649723a0c488c14060f607798e4d9c1d89a Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Sat, 16 Jan 2010 16:37:49 -0500 Subject: [PATCH] added Python's chainable comparisons, like: 10 > 5 > 1 --- lib/coffee_script/nodes.rb | 20 +++++++++++++++++-- lib/coffee_script/value.rb | 4 ++++ .../fixtures/execution/test_operations.coffee | 9 +++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/execution/test_operations.coffee diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 91204031..93ce1dda 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -26,7 +26,7 @@ module CoffeeScript # Provide a quick implementation of a children method. def self.children(*attributes) - attr_reader *attributes + attr_reader(*attributes) attrs = attributes.map {|a| "[@#{a}]" }.join(', ') class_eval "def children; [#{attrs}].flatten.compact; end" end @@ -205,7 +205,7 @@ module CoffeeScript def compile_node(o) indent = statement? ? idt : '' ending = statement? ? ';' : '' - write "#{indent}#{@value}#{ending}" + "#{indent}#{@value}#{ending}" end end @@ -355,6 +355,10 @@ module CoffeeScript properties? && @properties.last.is_a?(SliceNode) end + def unwrap + @properties.empty? ? @base : self + end + # Values are statements if their base is a statement. def statement? @base.is_a?(Node) && @base.statement? && !properties? @@ -550,6 +554,7 @@ module CoffeeScript :isnt => "!==", :not => '!' } + CHAINABLE = [:<, :>, :>=, :<=, :===, :'!==='] CONDITIONALS = [:'||=', :'&&='] PREFIX_OPERATORS = [:typeof, :delete] @@ -562,12 +567,23 @@ module CoffeeScript @second.nil? end + def chainable? + CHAINABLE.include?(operator.to_sym) && !unary? + end + def compile_node(o) + return write(compile_chain(o)) if chainable? && @first.unwrap.is_a?(OpNode) && @first.unwrap.chainable? return write(compile_conditional(o)) if CONDITIONALS.include?(@operator.to_sym) return write(compile_unary(o)) if unary? write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}") end + # Mimic Python's chained comparisons. See: + # http://docs.python.org/reference/expressions.html#notin + def compile_chain(o) + write("(#{@first.compile(o)}) && (#{@first.unwrap.second.compile(o)} #{@operator} #{@second.compile(o)})") + end + def compile_conditional(o) first, second = @first.compile(o), @second.compile(o) sym = @operator[0..1] diff --git a/lib/coffee_script/value.rb b/lib/coffee_script/value.rb index 05598145..8cb2207d 100644 --- a/lib/coffee_script/value.rb +++ b/lib/coffee_script/value.rb @@ -55,6 +55,10 @@ module CoffeeScript def statement_only? false end + + def contains? + false + end end end \ No newline at end of file diff --git a/test/fixtures/execution/test_operations.coffee b/test/fixtures/execution/test_operations.coffee new file mode 100644 index 00000000..0043b55d --- /dev/null +++ b/test/fixtures/execution/test_operations.coffee @@ -0,0 +1,9 @@ +# CoffeeScript's operations should be chainable, like Python's. + +print(500 > 50 > 5 > -5) + +print(true is not false is true is not false) + +print(10 < 20 > 10) + +print(50 > 10 > 5 is parseInt('5', 10)) \ No newline at end of file