From 1ba7c771360e177504cb8171e5e8dcc77d2c1481 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Fri, 25 Dec 2009 16:20:28 -0800 Subject: [PATCH] reorganizing test fixtures and adding range literals for array slices --- examples/code.coffee | 2 +- examples/poignant.coffee | 2 +- lib/coffee_script/grammar.y | 12 ++++++-- lib/coffee_script/lexer.rb | 2 +- lib/coffee_script/nodes.rb | 28 +++++++++++++++---- ...coffee => test_array_comprehension.coffee} | 0 ...coffee => test_assign_to_try_catch.coffee} | 0 ...super.coffee => test_calling_super.coffee} | 0 ...calls.coffee => test_chained_calls.coffee} | 0 .../fixtures/execution/test_everything.coffee | 2 +- ....coffee => test_fancy_if_statement.coffee} | 0 .../execution/test_ranges_and_slices.coffee | 0 test/fixtures/{ => generation}/each.coffee | 0 test/fixtures/{ => generation}/each.js | 0 test/fixtures/{ => generation}/each.tokens | 0 .../fixtures/{ => generation}/each_no_wrap.js | 0 .../{ => generation}/inner_comments.coffee | 0 .../{ => generation}/inner_comments.js | 0 test/unit/test_execution.rb | 2 +- test/unit/test_lexer.rb | 4 +-- test/unit/test_parser.rb | 14 +++++----- 21 files changed, 46 insertions(+), 22 deletions(-) rename test/fixtures/execution/{array_comprehension.coffee => test_array_comprehension.coffee} (100%) rename test/fixtures/execution/{assign_to_try_catch.coffee => test_assign_to_try_catch.coffee} (100%) rename test/fixtures/execution/{calling_super.coffee => test_calling_super.coffee} (100%) rename test/fixtures/execution/{chained_calls.coffee => test_chained_calls.coffee} (100%) rename test/fixtures/execution/{fancy_if_statement.coffee => test_fancy_if_statement.coffee} (100%) create mode 100644 test/fixtures/execution/test_ranges_and_slices.coffee rename test/fixtures/{ => generation}/each.coffee (100%) rename test/fixtures/{ => generation}/each.js (100%) rename test/fixtures/{ => generation}/each.tokens (100%) rename test/fixtures/{ => generation}/each_no_wrap.js (100%) rename test/fixtures/{ => generation}/inner_comments.coffee (100%) rename test/fixtures/{ => generation}/inner_comments.js (100%) diff --git a/examples/code.coffee b/examples/code.coffee index 27c6d17a..23458fd8 100644 --- a/examples/code.coffee +++ b/examples/code.coffee @@ -132,7 +132,7 @@ wednesday: => eat_breakfast(); go_to_work(); eat_dinner(); . # Array slice literals. zero_to_nine: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -three_to_six: zero_to_nine[3, 6] +three_to_six: zero_to_nine[3..6] # Multiline strings with inner quotes. story: "Lorem ipsum dolor \"sit\" amet, consectetuer adipiscing elit, diff --git a/examples/poignant.coffee b/examples/poignant.coffee index 7c9b05f5..973248ce 100644 --- a/examples/poignant.coffee +++ b/examples/poignant.coffee @@ -149,5 +149,5 @@ wipe_mutterings_from: sentence => while sentence.indexOf('(') >= 0 open: sentence.indexOf('(') - 1 close: sentence.indexOf(')') + 1 - sentence: sentence[0, open] + sentence[close, sentence.length]. + sentence: sentence[0..open] + sentence[close..sentence.length]. sentence. \ No newline at end of file diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index fbdd9efc..eecd8530 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -64,8 +64,7 @@ rule # The parts that are natural JavaScript expressions. PureExpression: - Literal - | Value + Value | Call | Code | Operation @@ -213,6 +212,7 @@ rule # Expressions that can be treated as values. Value: IDENTIFIER { result = ValueNode.new(val[0]) } + | Literal { result = ValueNode.new(val[0]) } | Array { result = ValueNode.new(val[0]) } | Object { result = ValueNode.new(val[0]) } | Parenthetical { result = ValueNode.new(val[0]) } @@ -234,7 +234,7 @@ rule # Array slice literal. Slice: - "[" Expression "," Expression "]" { result = SliceNode.new(val[1], val[3]) } + "[" Range "]" { result = SliceNode.new(val[1]) } ; # An object literal. @@ -273,6 +273,12 @@ rule SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) } ; + # The range literal. + Range: + Value "." "." Value { result = RangeNode.new(val[0], val[3]) } + | Value "." "." "." Value { result = RangeNode.new(val[0], val[4], true) } + ; + # The array literal. Array: "[" ArgList "]" { result = ArrayNode.new(val[1]) } diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 96a31877..fcf21618 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -75,7 +75,7 @@ module CoffeeScript # Keywords are special identifiers tagged with their own name, 'if' will result # in an [:IF, "if"] token tag = KEYWORDS.include?(identifier) ? identifier.upcase.to_sym : :IDENTIFIER - @tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' + @tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2][1] == '.') token(tag, identifier) @i += identifier.length end diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index a210f31f..29b35cc3 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -300,19 +300,37 @@ module CoffeeScript end end + # A range literal. Ranges can be used to extract portions (slices) of arrays, + # or to specify a range for array comprehensions. + class RangeNode + attr_reader :from, :to + + def initialize(from, to, exclusive=false) + @from, @to, @exclusive = from, to, exclusive + end + + def exclusive? + @exclusive + end + + end + # An array slice literal. Unlike JavaScript's Array#slice, the second parameter # specifies the index of the end of the slice (just like the first parameter) # is the index of the beginning. class SliceNode < Node - attr_reader :from, :to + attr_reader :range - def initialize(from, to) - @from, @to = from, to + def initialize(range) + @range = range end def compile(o={}) - o = super(o) - write(".slice(#{@from.compile(o)}, #{@to.compile(o)} + 1)") + o = super(o) + from = @range.from.compile(o) + to = @range.to.compile(o) + plus_part = @range.exclusive? ? '' : ' + 1' + write(".slice(#{from}, #{to}#{plus_part})") end end diff --git a/test/fixtures/execution/array_comprehension.coffee b/test/fixtures/execution/test_array_comprehension.coffee similarity index 100% rename from test/fixtures/execution/array_comprehension.coffee rename to test/fixtures/execution/test_array_comprehension.coffee diff --git a/test/fixtures/execution/assign_to_try_catch.coffee b/test/fixtures/execution/test_assign_to_try_catch.coffee similarity index 100% rename from test/fixtures/execution/assign_to_try_catch.coffee rename to test/fixtures/execution/test_assign_to_try_catch.coffee diff --git a/test/fixtures/execution/calling_super.coffee b/test/fixtures/execution/test_calling_super.coffee similarity index 100% rename from test/fixtures/execution/calling_super.coffee rename to test/fixtures/execution/test_calling_super.coffee diff --git a/test/fixtures/execution/chained_calls.coffee b/test/fixtures/execution/test_chained_calls.coffee similarity index 100% rename from test/fixtures/execution/chained_calls.coffee rename to test/fixtures/execution/test_chained_calls.coffee diff --git a/test/fixtures/execution/test_everything.coffee b/test/fixtures/execution/test_everything.coffee index f0a866dc..ca24c640 100644 --- a/test/fixtures/execution/test_everything.coffee +++ b/test/fixtures/execution/test_everything.coffee @@ -22,6 +22,6 @@ func: => c.list: l for l in d.text.split('') if l is '-'. - c.single: c.list[1, 1][0]. + c.single: c.list[1..1][0]. print(func() == '-') diff --git a/test/fixtures/execution/fancy_if_statement.coffee b/test/fixtures/execution/test_fancy_if_statement.coffee similarity index 100% rename from test/fixtures/execution/fancy_if_statement.coffee rename to test/fixtures/execution/test_fancy_if_statement.coffee diff --git a/test/fixtures/execution/test_ranges_and_slices.coffee b/test/fixtures/execution/test_ranges_and_slices.coffee new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/each.coffee b/test/fixtures/generation/each.coffee similarity index 100% rename from test/fixtures/each.coffee rename to test/fixtures/generation/each.coffee diff --git a/test/fixtures/each.js b/test/fixtures/generation/each.js similarity index 100% rename from test/fixtures/each.js rename to test/fixtures/generation/each.js diff --git a/test/fixtures/each.tokens b/test/fixtures/generation/each.tokens similarity index 100% rename from test/fixtures/each.tokens rename to test/fixtures/generation/each.tokens diff --git a/test/fixtures/each_no_wrap.js b/test/fixtures/generation/each_no_wrap.js similarity index 100% rename from test/fixtures/each_no_wrap.js rename to test/fixtures/generation/each_no_wrap.js diff --git a/test/fixtures/inner_comments.coffee b/test/fixtures/generation/inner_comments.coffee similarity index 100% rename from test/fixtures/inner_comments.coffee rename to test/fixtures/generation/inner_comments.coffee diff --git a/test/fixtures/inner_comments.js b/test/fixtures/generation/inner_comments.js similarity index 100% rename from test/fixtures/inner_comments.js rename to test/fixtures/generation/inner_comments.js diff --git a/test/unit/test_execution.rb b/test/unit/test_execution.rb index 8e9d79e8..7910e861 100644 --- a/test/unit/test_execution.rb +++ b/test/unit/test_execution.rb @@ -3,7 +3,7 @@ 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/ + ALLS_WELL = /\A\n?(true\n)+\Z/m def test_execution_of_coffeescript sources = ['test/fixtures/execution/*.coffee'].join(' ') diff --git a/test/unit/test_lexer.rb b/test/unit/test_lexer.rb index 09edfd87..5f8d52d2 100644 --- a/test/unit/test_lexer.rb +++ b/test/unit/test_lexer.rb @@ -43,8 +43,8 @@ class LexerTest < Test::Unit::TestCase end def test_lexing - tokens = @lex.tokenize(File.read('test/fixtures/each.coffee')) - assert tokens.inspect == File.read('test/fixtures/each.tokens') + tokens = @lex.tokenize(File.read('test/fixtures/generation/each.coffee')) + assert tokens.inspect == File.read('test/fixtures/generation/each.tokens') end end diff --git a/test/unit/test_parser.rb b/test/unit/test_parser.rb index d356dfa1..ef0b02a1 100644 --- a/test/unit/test_parser.rb +++ b/test/unit/test_parser.rb @@ -49,7 +49,7 @@ class ParserTest < Test::Unit::TestCase assert nodes.first.is_a? ForNode assert nodes.first.body.literal == 'i' assert nodes.first.filter.operator == '===' - assert nodes.first.source.literal.objects.last.value == "5" + assert nodes.first.source.literal.objects.last.literal.value == "5" end def test_parsing_comment @@ -58,23 +58,23 @@ class ParserTest < Test::Unit::TestCase end def test_parsing_inner_comments - nodes = @par.parse(File.read('test/fixtures/inner_comments.coffee')) - assert nodes.compile == File.read('test/fixtures/inner_comments.js') + nodes = @par.parse(File.read('test/fixtures/generation/inner_comments.coffee')) + assert nodes.compile == File.read('test/fixtures/generation/inner_comments.js') end def test_parsing - nodes = @par.parse(File.read('test/fixtures/each.coffee')) + nodes = @par.parse(File.read('test/fixtures/generation/each.coffee')) assign = nodes.expressions[1] assert assign.is_a? AssignNode assert assign.variable.literal == '_' assert assign.value.is_a? CodeNode assert assign.value.params == ['obj', 'iterator', 'context'] - assert nodes.compile == File.read('test/fixtures/each.js') + assert nodes.compile == File.read('test/fixtures/generation/each.js') end def test_no_wrap - nodes = @par.parse(File.read('test/fixtures/each.coffee')) - assert nodes.compile(:no_wrap => true) == File.read('test/fixtures/each_no_wrap.js') + nodes = @par.parse(File.read('test/fixtures/generation/each.coffee')) + assert nodes.compile(:no_wrap => true) == File.read('test/fixtures/generation/each_no_wrap.js') end end