From f6c8e81ea6d8a3c0016b411c3f34fa5d4ea133ba Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Sat, 16 Jan 2010 23:03:54 -0500 Subject: [PATCH] the existential operator can now be used infix as well --- lib/coffee_script/grammar.y | 1 + lib/coffee_script/nodes.rb | 23 +++++++++++++------ test/fixtures/execution/test_existence.coffee | 20 +++++++++++++++- .../fixtures/execution/test_operations.coffee | 10 -------- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 0817762f..1cb1337e 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -179,6 +179,7 @@ rule | Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) } | Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) } | Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) } + | Expression '?' Expression { result = OpNode.new(val[1], val[0], val[2]) } | Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) } | Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) } diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 84726d74..c446a601 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -556,7 +556,7 @@ module CoffeeScript :not => '!' } CHAINABLE = [:<, :>, :>=, :<=, :===, :'!==='] - CONDITIONALS = [:'||=', :'&&=', :'?='] + ASSIGNMENT = [:'||=', :'&&=', :'?='] PREFIX_OPERATORS = [:typeof, :delete] def initialize(operator, first, second=nil, flip=false) @@ -574,8 +574,9 @@ module CoffeeScript 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_assignment(o)) if ASSIGNMENT.include?(@operator.to_sym) return write(compile_unary(o)) if unary? + return write(compile_existence(o)) if @operator == '?' write("#{@first.compile(o)} #{@operator} #{@second.compile(o)}") end @@ -588,17 +589,22 @@ module CoffeeScript @first.second = ParentheticalNode.new(AssignNode.new(temp, shared)) shared = temp end - write("(#{@first.compile(o)}) && (#{shared.compile(o)} #{@operator} #{@second.compile(o)})") + "(#{@first.compile(o)}) && (#{shared.compile(o)} #{@operator} #{@second.compile(o)})" end - def compile_conditional(o) + def compile_assignment(o) first, second = @first.compile(o), @second.compile(o) o[:scope].find(first) if @first.unwrap.is_a?(Value) sym = @operator[0..1] - return "#{first} = (#{first} !== undefined && #{first} !== null) ? #{first} : #{second}" if @operator == '?=' + return "#{first} = #{ExistenceNode.compile_test(first)} ? #{first} : #{second}" if @operator == '?=' "#{first} = #{first} #{sym} #{second}" end + def compile_existence(o) + first, second = @first.compile(o), @second.compile(o) + "#{ExistenceNode.compile_test(first)} ? #{first} : #{second}" + end + def compile_unary(o) space = PREFIX_OPERATORS.include?(@operator.to_sym) ? ' ' : '' parts = [@operator.to_s, space, @first.compile(o)] @@ -890,13 +896,16 @@ module CoffeeScript class ExistenceNode < Node children :expression + def self.compile_test(variable) + "(typeof #{variable} !== \"undefined\" && #{variable} !== null)" + end + def initialize(expression) @expression = expression end def compile_node(o) - val = @expression.compile(o) - write("(typeof #{val} !== \"undefined\" && #{val} !== null)") + write(ExistenceNode.compile_test(@expression.compile(o))) end end diff --git a/test/fixtures/execution/test_existence.coffee b/test/fixtures/execution/test_existence.coffee index 47bfbfab..7450b257 100644 --- a/test/fixtures/execution/test_existence.coffee +++ b/test/fixtures/execution/test_existence.coffee @@ -2,4 +2,22 @@ print(if my_special_variable? then false else true) my_special_variable: false -print(if my_special_variable? then true else false) \ No newline at end of file +print(if my_special_variable? then true else false) + + +# Existential assignment. + +a: 5 +a: null +a ?= 10 +b ?= 10 + +print(a is 10 and b is 10) + + +# The existential operator. + +z: null +x: z ? "EX" + +print(z is null and x is "EX") \ No newline at end of file diff --git a/test/fixtures/execution/test_operations.coffee b/test/fixtures/execution/test_operations.coffee index 0b996da6..56f57b6c 100644 --- a/test/fixtures/execution/test_operations.coffee +++ b/test/fixtures/execution/test_operations.coffee @@ -16,13 +16,3 @@ i: 0 func: => i++ print(1 > func() < 1) - - -# The conditional assignment based on existence. - -a: 5 -a: null -a ?= 10 -b ?= 10 - -print(a is 10 and b is 10) \ No newline at end of file