From 1d2bb3b2be093034b1cdbc9475e6420181d51c42 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Thu, 31 Dec 2009 17:50:12 -0500 Subject: [PATCH] adding splats to function definitions --- .../Syntaxes/CoffeeScript.tmLanguage | 4 +-- lib/coffee_script/grammar.y | 11 +++++-- lib/coffee_script/lexer.rb | 16 +++++++--- lib/coffee_script/nodes.rb | 30 ++++++++++++++++++- test/fixtures/execution/test_splats.coffee | 6 ++++ 5 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/execution/test_splats.coffee diff --git a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage index 88bb3bff..e2922226 100644 --- a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage +++ b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage @@ -39,7 +39,7 @@ comment match stuff like: funcName: => … match - ([a-zA-Z0-9_?.$]*)\s*(=|:)\s*([\w,\s]*?)\s*(=>) + ([a-zA-Z0-9_?.$*]*)\s*(=|:)\s*([\w,\s]*?)\s*(=>) name meta.function.coffee @@ -60,7 +60,7 @@ comment match stuff like: a => … match - ([a-zA-Z0-9_?., $]*)\s*(=>) + ([a-zA-Z0-9_?., $*]*)\s*(=>) name meta.inline.function.coffee diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 2b747738..049482f3 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -5,7 +5,7 @@ token IF ELSE UNLESS token NUMBER STRING REGEX token TRUE FALSE YES NO ON OFF token IDENTIFIER PROPERTY_ACCESS -token CODE PARAM NEW RETURN +token CODE PARAM SPLAT NEW RETURN token TRY CATCH FINALLY THROW token BREAK CONTINUE token FOR IN WHILE @@ -187,8 +187,13 @@ rule # The parameters to a function definition. ParamList: - PARAM { result = val } - | ParamList "," PARAM { result = val[0] << val[2] } + Param { result = val } + | ParamList "," Param { result = val[0] << val[2] } + ; + + Param: + PARAM + | SPLAT { result = SplatNode.new(val[0]) } ; # Expressions that can be treated as values. diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 48a0a32f..02341497 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -190,15 +190,23 @@ module CoffeeScript # A source of ambiguity in our grammar was parameter lists in function # definitions (as opposed to argument lists in function calls). Tag - # parameter identifiers in order to avoid this. + # parameter identifiers in order to avoid this. Also, parameter lists can + # make use of splats. def tag_parameters - index = 0 + i = 0 loop do - tok = @tokens[index -= 1] + i -= 1 + tok, prev = @tokens[i], @tokens[i - 1] return if !tok next if tok[0] == ',' return if tok[0] != :IDENTIFIER - tok[0] = :PARAM + if prev && prev[0] == '*' + tok[0] = :SPLAT + @tokens.delete_at(i - 1) + i -= 1 + else + tok[0] = :PARAM + end end end diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 060ae24d..d2683292 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -64,6 +64,11 @@ module CoffeeScript self end + def unshift(node) + @expressions.unshift(node) + self + end + # If this Expressions consists of a single node, pull it back out. def unwrap @expressions.length == 1 ? @expressions.first : self @@ -389,7 +394,7 @@ module CoffeeScript o[:scope].find(name) unless @variable.properties? return write(@value.compile(o)) if @value.custom_assign? val = "#{name} = #{@value.compile(o)}" - write(o[:return] && !@value.custom_return? ? "return (#{val})" : val) + write(o[:return] && !@value.custom_return? ? "#{o[:indent]}return (#{val})" : val) end end @@ -459,12 +464,35 @@ module CoffeeScript o.delete(:no_wrap) name = o.delete(:immediate_assign) @params.each {|id| o[:scope].parameter(id.to_s) } + if @params.last.is_a?(SplatNode) + splat = @params.pop + splat.index = @params.length + @body.unshift(splat) + end code = @body.compile(o, :code) name_part = name ? " #{name}" : '' write("function#{name_part}(#{@params.join(', ')}) {\n#{code}\n#{indent}}") end end + # A parameter splat in a function definition. + class SplatNode < Node + attr_accessor :index + attr_reader :name + + def initialize(name) + @name = name + end + + def to_s + @name + end + + def compile(o={}) + "var #{@name} = Array.prototype.slice.call(arguments, #{@index})" + end + end + # An object literal. class ObjectNode < Node attr_reader :properties diff --git a/test/fixtures/execution/test_splats.coffee b/test/fixtures/execution/test_splats.coffee new file mode 100644 index 00000000..6b273137 --- /dev/null +++ b/test/fixtures/execution/test_splats.coffee @@ -0,0 +1,6 @@ +func: first, second, *rest => + rest.join(' ') + +result: func(1, 2, 3, 4, 5) + +print(result is "3 4 5") \ No newline at end of file