adding splats as arguments to function calls
This commit is contained in:
parent
abfc9f5a2d
commit
409283a30f
|
@ -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 SPLAT NEW RETURN
|
||||
token CODE PARAM NEW RETURN
|
||||
token TRY CATCH FINALLY THROW
|
||||
token BREAK CONTINUE
|
||||
token FOR IN WHILE
|
||||
|
@ -19,7 +19,7 @@ token INDENT OUTDENT
|
|||
|
||||
# Declare order of operations.
|
||||
prechigh
|
||||
nonassoc UMINUS NOT '!' '!!' '~' '++' '--'
|
||||
nonassoc UMINUS SPLAT NOT '!' '!!' '~' '++' '--'
|
||||
left '*' '/' '%'
|
||||
left '+' '-'
|
||||
left '<<' '>>' '>>>'
|
||||
|
@ -70,6 +70,7 @@ rule
|
|||
| For
|
||||
| Switch
|
||||
| Extends
|
||||
| Splat
|
||||
| Comment
|
||||
;
|
||||
|
||||
|
@ -193,7 +194,11 @@ rule
|
|||
|
||||
Param:
|
||||
PARAM
|
||||
| SPLAT { result = SplatNode.new(val[0]) }
|
||||
| '*' PARAM = SPLAT { result = ParamSplatNode.new(val[1]) }
|
||||
;
|
||||
|
||||
Splat:
|
||||
'*' Value = SPLAT { result = ArgSplatNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# Expressions that can be treated as values.
|
||||
|
|
|
@ -196,17 +196,11 @@ module CoffeeScript
|
|||
i = 0
|
||||
loop do
|
||||
i -= 1
|
||||
tok, prev = @tokens[i], @tokens[i - 1]
|
||||
tok = @tokens[i]
|
||||
return if !tok
|
||||
next if tok[0] == ','
|
||||
next if ['*', ','].include?(tok[0])
|
||||
return if tok[0] != :IDENTIFIER
|
||||
if prev && prev[0] == '*'
|
||||
tok[0] = :SPLAT
|
||||
@tokens.delete_at(i - 1)
|
||||
i -= 1
|
||||
else
|
||||
tok[0] = :PARAM
|
||||
end
|
||||
tok[0] = :PARAM
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -212,11 +212,16 @@ module CoffeeScript
|
|||
@variable == :super
|
||||
end
|
||||
|
||||
def prefix
|
||||
@new ? "new " : ''
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
@splat = @arguments.detect {|a| a.is_a?(ArgSplatNode) }
|
||||
return write(compile_splat(o)) if @splat
|
||||
args = @arguments.map{|a| a.compile(o) }.join(', ')
|
||||
return write(compile_super(args, o)) if super?
|
||||
prefix = @new ? "new " : ''
|
||||
write("#{prefix}#{@variable.compile(o)}(#{args})")
|
||||
end
|
||||
|
||||
|
@ -225,6 +230,17 @@ module CoffeeScript
|
|||
arg_part = args.empty? ? '' : ", #{args}"
|
||||
"#{o[:proto_assign]}.__superClass__.#{methname}.call(this#{arg_part})"
|
||||
end
|
||||
|
||||
def compile_splat(o)
|
||||
meth = @variable.compile(o)
|
||||
obj = @variable.source || 'this'
|
||||
args = @arguments.map do |arg|
|
||||
code = arg.compile(o)
|
||||
code = arg == @splat ? code : "[#{code}]"
|
||||
arg.equal?(@arguments.first) ? code : ".concat(#{code})"
|
||||
end
|
||||
"#{prefix}#{meth}.apply(#{obj}, #{args.join('')})"
|
||||
end
|
||||
end
|
||||
|
||||
# Node to extend an object's prototype with an ancestor object.
|
||||
|
@ -247,7 +263,7 @@ module CoffeeScript
|
|||
|
||||
# A value, indexed or dotted into, or vanilla.
|
||||
class ValueNode < Node
|
||||
attr_reader :literal, :properties, :last
|
||||
attr_reader :literal, :properties, :last, :source
|
||||
|
||||
def initialize(literal, properties=[])
|
||||
@literal, @properties = literal, properties
|
||||
|
@ -280,6 +296,7 @@ module CoffeeScript
|
|||
val.respond_to?(:compile) ? val.compile(o) : val.to_s
|
||||
end
|
||||
@last = parts.last
|
||||
@source = parts.length > 1 ? parts[0...-1].join('') : nil
|
||||
write(parts.join(''))
|
||||
end
|
||||
end
|
||||
|
@ -463,7 +480,7 @@ module CoffeeScript
|
|||
o.delete(:assign)
|
||||
o.delete(:no_wrap)
|
||||
name = o.delete(:immediate_assign)
|
||||
if @params.last.is_a?(SplatNode)
|
||||
if @params.last.is_a?(ParamSplatNode)
|
||||
splat = @params.pop
|
||||
splat.index = @params.length
|
||||
@body.unshift(splat)
|
||||
|
@ -476,7 +493,7 @@ module CoffeeScript
|
|||
end
|
||||
|
||||
# A parameter splat in a function definition.
|
||||
class SplatNode < Node
|
||||
class ParamSplatNode < Node
|
||||
attr_accessor :index
|
||||
attr_reader :name
|
||||
|
||||
|
@ -486,10 +503,23 @@ module CoffeeScript
|
|||
|
||||
def compile(o={})
|
||||
o[:scope].find(@name)
|
||||
"#{@name} = Array.prototype.slice.call(arguments, #{@index})"
|
||||
write("#{@name} = Array.prototype.slice.call(arguments, #{@index})")
|
||||
end
|
||||
end
|
||||
|
||||
class ArgSplatNode < Node
|
||||
attr_reader :value
|
||||
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
write(@value.compile(o))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# An object literal.
|
||||
class ObjectNode < Node
|
||||
attr_reader :properties
|
||||
|
|
|
@ -3,4 +3,33 @@ func: first, second, *rest =>
|
|||
|
||||
result: func(1, 2, 3, 4, 5)
|
||||
|
||||
print(result is "3 4 5")
|
||||
print(result is "3 4 5")
|
||||
|
||||
|
||||
gold: silver: bronze: the_field: null
|
||||
|
||||
medalists: first, second, third, *rest =>
|
||||
gold: first
|
||||
silver: second
|
||||
bronze: third
|
||||
the_field: rest
|
||||
|
||||
contenders: [
|
||||
"Michael Phelps"
|
||||
"Liu Xiang"
|
||||
"Yao Ming"
|
||||
"Allyson Felix"
|
||||
"Shawn Johnson"
|
||||
"Roman Sebrle"
|
||||
"Guo Jingjing"
|
||||
"Tyson Gay"
|
||||
"Asafa Powell"
|
||||
"Usain Bolt"
|
||||
]
|
||||
|
||||
medalists("Mighty Mouse", *contenders)
|
||||
|
||||
print(gold is "Mighty Mouse")
|
||||
print(silver is "Michael Phelps")
|
||||
print(bronze is "Liu Xiang")
|
||||
print(the_field.length is 8)
|
|
@ -2,30 +2,35 @@ require 'test_helper'
|
|||
|
||||
class ExecutionTest < Test::Unit::TestCase
|
||||
|
||||
NO_WARNINGS = /\A(0 error\(s\), 0 warning\(s\)\n)+\Z/
|
||||
ALLS_WELL = /\A\n?(true\n)+\Z/m
|
||||
NO_WARNINGS = "0 error(s), 0 warning(s)"
|
||||
|
||||
# This is by far the most important test. It evaluates all of the
|
||||
# CoffeeScript in test/fixtures/execution, ensuring that all our
|
||||
# syntax actually works.
|
||||
def test_execution_of_coffeescript
|
||||
sources = ['test/fixtures/execution/*.coffee'].join(' ')
|
||||
assert `bin/coffee -r #{sources}`.match(ALLS_WELL)
|
||||
(`bin/coffee -r #{sources}`).split("\n").each do |line|
|
||||
assert line == "true"
|
||||
end
|
||||
end
|
||||
|
||||
def test_lintless_coffeescript
|
||||
lint_results = `bin/coffee -l test/fixtures/execution/*.coffee`
|
||||
assert lint_results.match(NO_WARNINGS)
|
||||
no_warnings `bin/coffee -l test/fixtures/execution/*.coffee`
|
||||
end
|
||||
|
||||
def test_lintless_examples
|
||||
lint_results = `bin/coffee -l examples/*.coffee`
|
||||
assert lint_results.match(NO_WARNINGS)
|
||||
no_warnings `bin/coffee -l examples/*.coffee`
|
||||
end
|
||||
|
||||
def test_lintless_documentation
|
||||
lint_results = `bin/coffee -l documentation/coffee/*.coffee`
|
||||
assert lint_results.match(NO_WARNINGS)
|
||||
no_warnings `bin/coffee -l documentation/coffee/*.coffee`
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def no_warnings(output)
|
||||
output.split("\n").each {|line| assert line == NO_WARNINGS }
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue