diff --git a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage index 23858937..5d35aa10 100644 --- a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage +++ b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage @@ -204,7 +204,7 @@ match - \b(break|catch|continue|else|finally|for|if|return|switch|then|throw|try|unless|when|while)\b + \b(break|by|catch|continue|else|finally|for|if|return|switch|then|throw|try|unless|when|while)\b name keyword.control.coffee diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 30351fda..61c3acd2 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -8,7 +8,7 @@ token IDENTIFIER PROPERTY_ACCESS token CODE PARAM NEW RETURN token TRY CATCH FINALLY THROW token BREAK CONTINUE -token FOR IN WHILE +token FOR IN BY WHILE token SWITCH WHEN token DELETE INSTANCEOF TYPEOF token SUPER EXTENDS @@ -32,7 +32,7 @@ prechigh left '.' right INDENT left OUTDENT - right WHEN IN + right WHEN IN BY right THROW FOR WHILE NEW SUPER ELSE left UNLESS EXTENDS IF left ASSIGN '||=' '&&=' @@ -321,8 +321,8 @@ rule # Looks a little confusing, check nodes.rb for the arguments to ForNode. For: Expression FOR - ForVariables ForSource { result = ForNode.new(val[0], val[3][0], val[2][0], val[3][1], val[2][1]) } - | FOR ForVariables ForSource Block { result = ForNode.new(val[3], val[2][0], val[1][0], val[2][1], val[1][1]) } + ForVariables ForSource { result = ForNode.new(val[0], val[3], val[2][0], val[2][1]) } + | FOR ForVariables ForSource Block { result = ForNode.new(val[3], val[2], val[1][0], val[1][1]) } ; # An array comprehension has variables for the current element and index. @@ -333,9 +333,11 @@ rule # The source of the array comprehension can optionally be filtered. ForSource: - IN Expression { result = [val[1]] } - | IN Expression - WHEN Expression { result = [val[1], val[3]] } + IN Expression { result = {:source => val[1]} } + | ForSource + WHEN Expression { result = val[0].merge(:filter => val[2]) } + | ForSource + BY Expression { result = val[0].merge(:step => val[2]) } ; # Switch/When blocks. diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 0c9a202a..3421b1b0 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -12,7 +12,7 @@ module CoffeeScript "new", "return", "try", "catch", "finally", "throw", "break", "continue", - "for", "in", "where", "while", + "for", "in", "by", "where", "while", "switch", "when", "super", "extends", "delete", "instanceof", "typeof"] diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 14b72820..9b701e5c 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -360,11 +360,12 @@ module CoffeeScript write("#{idt}#{@from_var} = #{from_val};\n#{idt}#{@to_var} = #{to_val};\n#{idt}") end - def compile(o, idx=nil) + def compile(o, idx=nil, step=nil) raise SyntaxError, "unexpected range literal" unless idx vars = "#{idx}=#{@from_var}" + step = step ? step.compile(o) : '1' compare = "(#{@from_var} <= #{@to_var} ? #{idx} #{less_operator} #{@to_var} : #{idx} #{greater_operator} #{@to_var})" - incr = "(#{@from_var} <= #{@to_var} ? #{idx} += 1 : #{idx} -= 1)" + incr = "(#{@from_var} <= #{@to_var} ? #{idx} += #{step} : #{idx} -= #{step})" write("#{vars}; #{compare}; #{incr}") end @@ -609,10 +610,13 @@ module CoffeeScript custom_return custom_assign - attr_reader :body, :source, :name, :filter, :index + attr_reader :body, :source, :name, :index, :filter, :step - def initialize(body, source, name, filter, index=nil) - @body, @source, @name, @filter, @index = body, source, name, filter, index + def initialize(body, source, name, index=nil) + @body, @name, @index = body, name, index + @source = source[:source] + @filter = source[:filter] + @step = source[:step] end def line_ending @@ -634,7 +638,7 @@ module CoffeeScript var_part, pre_cond, post_cond = '', '', '' index_var = scope.free_variable source_part = @source.compile_variables(o) - for_part = "#{index_var}=0, #{@source.compile(o, ivar)}, #{index_var}++" + for_part = "#{index_var}=0, #{@source.compile(o, ivar, @step)}, #{index_var}++" else index_var = nil body_dent = o[:indent] + TAB + TAB diff --git a/test/fixtures/execution/test_range_comprehension.coffee b/test/fixtures/execution/test_range_comprehension.coffee index e4856201..8917550b 100644 --- a/test/fixtures/execution/test_range_comprehension.coffee +++ b/test/fixtures/execution/test_range_comprehension.coffee @@ -12,4 +12,9 @@ j = 5 result: for j in [j..(j+3)] j -print(result.join(' ') is '5 6 7 8') \ No newline at end of file +print(result.join(' ') is '5 6 7 8') + +# With range comprehensions, you can loop in steps. +results: x for x in [0..25] by 5 + +print(results.join(' ') is '0 5 10 15 20 25') \ No newline at end of file