diff --git a/documentation/coffee/array_comprehensions.coffee b/documentation/coffee/array_comprehensions.coffee index ffccbfee..fb4934bf 100644 --- a/documentation/coffee/array_comprehensions.coffee +++ b/documentation/coffee/array_comprehensions.coffee @@ -3,3 +3,6 @@ lunch: food.eat() for food in ['toast', 'cheese', 'wine']. # Zebra-stripe a table. highlight(row) for row, i in table if i % 2 is 0. + +# Check the first one hundred combinations. +lockpick(combinations[i]) for i in [1..100]. \ No newline at end of file diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index d3c7b862..a0946f65 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -68,6 +68,7 @@ rule | Call | Code | Operation + | Range ; # We have to take extra care to convert these statements into expressions. @@ -224,7 +225,7 @@ rule Accessor: PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) } | Index { result = val[0] } - | Slice { result = val[0] } + | Range { result = SliceNode.new(val[0]) } ; # Indexing into an object or array. @@ -232,11 +233,6 @@ rule "[" Expression "]" { result = IndexNode.new(val[1]) } ; - # Array slice literal. - Slice: - "[" Range "]" { result = SliceNode.new(val[1]) } - ; - # An object literal. Object: "{" AssignList "}" { result = ObjectNode.new(val[1]) } @@ -275,8 +271,8 @@ rule # The range literal. Range: - Value "." "." Value { result = RangeNode.new(val[0], val[3]) } - | Value "." "." "." Value { result = RangeNode.new(val[0], val[4], true) } + "[" Value "." "." Value "]" { result = RangeNode.new(val[1], val[4]) } + | "[" Value "." "." "." Value "]" { result = RangeNode.new(val[1], val[5], true) } ; # The array literal. diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 69b9f6e4..d56099d7 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -307,8 +307,8 @@ module CoffeeScript # A range literal. Ranges can be used to extract portions (slices) of arrays, # or to specify a range for array comprehensions. - # Because there's no corresponding concept in JavaScript, RangeNodes are never - # compiled directly, just used by other nodes that accept ranges. + # RangeNodes get expanded into the equivalent array, if not used to index + # a slice or define an array comprehension. class RangeNode attr_reader :from, :to @@ -320,6 +320,10 @@ module CoffeeScript @exclusive end + def compile(o={}) + write() + end + end # An array slice literal. Unlike JavaScript's Array#slice, the second parameter @@ -526,18 +530,27 @@ module CoffeeScript def compile(o={}) o = super(o) + range = @source.is_a?(RangeNode) scope = o[:scope] name_found = scope.find(@name) index_found = @index && scope.find(@index) svar = scope.free_variable - ivar = scope.free_variable + ivar = range ? name : scope.free_variable lvar = scope.free_variable rvar = scope.free_variable index_name = @index ? @index : nil - source_part = "#{svar} = #{@source.compile(o)};" - for_part = "#{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++" - var_part = "\n#{o[:indent] + TAB}#{@name} = #{svar}[#{ivar}];\n" - index_part = @index ? "#{o[:indent] + TAB}#{index_name} = #{ivar};\n" : '' + if range + source_part = '' + operator = @source.exclusive? ? '<' : '<=' + for_part = "#{ivar}=#{@source.from.compile(o)}, #{lvar}=#{@source.to.compile(o)}; #{ivar}#{operator}#{lvar}; #{ivar}++" + var_part = '' + index_part = '' + else + source_part = "#{svar} = #{@source.compile(o)};\n#{o[:indent]}" + for_part = "#{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++" + var_part = "\n#{o[:indent] + TAB}#{@name} = #{svar}[#{ivar}];" + index_part = @index ? "\n#{o[:indent] + TAB}#{index_name} = #{ivar};" : '' + end body = @body suffix = ';' set_result = "#{rvar} = [];\n#{o[:indent]}" @@ -560,7 +573,7 @@ module CoffeeScript return_result = "\n#{o[:indent]}#{return_result};" indent = o[:indent] + TAB body = body.compile(o.merge(:indent => indent)) - write("#{source_part}\n#{o[:indent]}#{set_result}for (#{for_part}) {#{var_part}#{index_part}#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}") + write("#{source_part}#{set_result}for (#{for_part}) {#{var_part}#{index_part}\n#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}") end end diff --git a/test/fixtures/execution/test_range_comprehension.coffee b/test/fixtures/execution/test_range_comprehension.coffee new file mode 100644 index 00000000..66ddefb2 --- /dev/null +++ b/test/fixtures/execution/test_range_comprehension.coffee @@ -0,0 +1,5 @@ +nums: i * 3 for i in [1..3]. + +result: nums.join(', ') + +print(result is '3, 6, 9') \ No newline at end of file diff --git a/test/unit/test_execution.rb b/test/unit/test_execution.rb index 7910e861..f5335e57 100644 --- a/test/unit/test_execution.rb +++ b/test/unit/test_execution.rb @@ -20,4 +20,9 @@ class ExecutionTest < Test::Unit::TestCase assert lint_results.match(NO_WARNINGS) end + def test_lintless_documentation + lint_results = `bin/coffee-script -l documentation/coffee/*.coffee` + assert lint_results.match(NO_WARNINGS) + end + end