From e77e5206079efc1b74a32a0fcedfe663436a5b2a Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 13 Jan 2010 23:24:45 -0500 Subject: [PATCH] CoffeeScript 0.2.5 is on the books --- coffee-script.gemspec | 4 +- documentation/coffee/long_arrow.coffee | 6 + documentation/coffee/switch.coffee | 9 +- documentation/coffee/while.coffee | 11 +- documentation/index.html.erb | 37 ++++- documentation/js/array_comprehensions.js | 6 +- documentation/js/expressions_comprehension.js | 3 +- documentation/js/long_arrow.js | 20 +++ documentation/js/object_comprehensions.js | 3 +- documentation/js/overview.js | 2 +- documentation/js/switch.js | 12 +- documentation/js/while.js | 23 ++- index.html | 154 ++++++++++++++---- lib/coffee-script.rb | 2 +- lib/coffee_script/lexer.rb | 11 +- lib/coffee_script/nodes.rb | 2 +- package.json | 2 +- 17 files changed, 237 insertions(+), 70 deletions(-) create mode 100644 documentation/coffee/long_arrow.coffee create mode 100644 documentation/js/long_arrow.js diff --git a/coffee-script.gemspec b/coffee-script.gemspec index 8a4aecc4..acbab9a1 100644 --- a/coffee-script.gemspec +++ b/coffee-script.gemspec @@ -1,7 +1,7 @@ Gem::Specification.new do |s| s.name = 'coffee-script' - s.version = '0.2.4' # Keep version in sync with coffee-script.rb - s.date = '2010-1-12' + s.version = '0.2.5' # Keep version in sync with coffee-script.rb + s.date = '2010-1-13' s.homepage = "http://jashkenas.github.com/coffee-script/" s.summary = "The CoffeeScript Compiler" diff --git a/documentation/coffee/long_arrow.coffee b/documentation/coffee/long_arrow.coffee new file mode 100644 index 00000000..86c64e9b --- /dev/null +++ b/documentation/coffee/long_arrow.coffee @@ -0,0 +1,6 @@ +Account: customer, cart => + this.customer: customer + this.cart: cart + + $('.shopping_cart').bind('click') event ==> + this.customer.purchase(this.cart) \ No newline at end of file diff --git a/documentation/coffee/switch.coffee b/documentation/coffee/switch.coffee index 7d9605e6..3478883d 100644 --- a/documentation/coffee/switch.coffee +++ b/documentation/coffee/switch.coffee @@ -1,9 +1,10 @@ switch day - when "Tuesday" then eat_breakfast() - when "Wednesday" then go_to_the_park() - when "Saturday" + when "Mon" then go_to_work() + when "Tue" then go_to_the_park() + when "Thu" then go_ice_fishing() + when "Fri", "Sat" if day is bingo_day go_to_bingo() go_dancing() - when "Sunday" then go_to_church() + when "Sun" then go_to_church() else go_to_work() \ No newline at end of file diff --git a/documentation/coffee/while.coffee b/documentation/coffee/while.coffee index 3ca4a984..625e6ed6 100644 --- a/documentation/coffee/while.coffee +++ b/documentation/coffee/while.coffee @@ -1,5 +1,8 @@ -while demand > supply - sell() - restock() +if this.studying_economics + while supply > demand then buy() + while supply < demand then sell() -while supply > demand then buy() \ No newline at end of file +num: 6 +lyrics: while num -= 1 + num + " little monkeys, jumping on the bed. + One fell out and bumped his head." diff --git a/documentation/index.html.erb b/documentation/index.html.erb index f2ae3513..0927d89f 100644 --- a/documentation/index.html.erb +++ b/documentation/index.html.erb @@ -51,7 +51,7 @@

Latest Version: - 0.2.4 + 0.2.5

Table of Contents

@@ -76,6 +76,7 @@ Inheritance, and Calling Super from a Subclass
Blocks
Pattern Matching
+ Function Binding
Embedded JavaScript
Switch/When/Else
Try/Catch/Finally
@@ -387,9 +388,12 @@ coffee --print app/scripts/*.coffee > concatenation.js

While Loops - The only low-level loop that CoffeeScript provides is the while loop. + The only low-level loop that CoffeeScript provides is the while loop. The + main difference from JavaScript is that the while loop can be used + as an expression, returning an array containing the result of each iteration + through the loop.

- <%= code_for('while') %> + <%= code_for('while', 'lyrics.join("\n")') %>

Other JavaScript loops, such as for loops and do-while loops can be mimicked by variations on while, but the hope is that you @@ -528,6 +532,17 @@ coffee --print app/scripts/*.coffee > concatenation.js

<%= code_for('object_extraction', 'poet + " — " + street') %> +

+ Function binding + The long arrow ==> can be used to both define a function, and to bind + it to the current value of this, right on the spot. This is helpful + when using callback-based libraries like Prototype or jQuery, for creating + iterator functions to pass to each, or event-handler functions + to use with bind. Functions created with the long arrow are able to access + properties of the this where they're defined. +

+ <%= code_for('long_arrow') %> +

Embedded JavaScript Hopefully, you'll never need to use it, but if you ever need to intersperse @@ -546,6 +561,11 @@ coffee --print app/scripts/*.coffee > concatenation.js in a returnable, assignable expression. The format is: switch condition, when clauses, else the default case.

+

+ As in Ruby, switch statements in CoffeeScript can take multiple + values for each when clause. If any of the values match, the clause + runs. +

<%= code_for('switch') %>

@@ -624,7 +644,16 @@ coffee --print app/scripts/*.coffee > concatenation.js

Change Log

- + +

+ 0.2.5 + The conditions in switch statements can now take multiple values at once — + If any of them are true, the case will run. Added the long arrow ==>, + which defines and immediately binds a function to this. While loops can + now be used as expressions, in the same way that comprehensions can. Splats + can be used within pattern matches to soak up the rest of an array. +

+

0.2.4 Added ECMAScript Harmony style destructuring assignment, for dealing with diff --git a/documentation/js/array_comprehensions.js b/documentation/js/array_comprehensions.js index fe7d5db6..fae31ce9 100644 --- a/documentation/js/array_comprehensions.js +++ b/documentation/js/array_comprehensions.js @@ -3,7 +3,7 @@ // Eat lunch. lunch = (function() { __a = []; __b = ['toast', 'cheese', 'wine']; - for (__c=0; __c<__b.length; __c++) { + for (__c = 0; __c < __b.length; __c++) { food = __b[__c]; __a.push(eat(food)); } @@ -11,10 +11,10 @@ })(); // Naive collision detection. __d = asteroids; - for (__e=0; __e<__d.length; __e++) { + for (__e = 0; __e < __d.length; __e++) { roid = __d[__e]; __f = asteroids; - for (__g=0; __g<__f.length; __g++) { + for (__g = 0; __g < __f.length; __g++) { roid2 = __f[__g]; if (roid !== roid2) { if (roid.overlaps(roid2)) { diff --git a/documentation/js/expressions_comprehension.js b/documentation/js/expressions_comprehension.js index 2d159d9f..294c3db4 100644 --- a/documentation/js/expressions_comprehension.js +++ b/documentation/js/expressions_comprehension.js @@ -1,10 +1,11 @@ (function(){ var __a, __b, globals, name; + var __hasProp = Object.prototype.hasOwnProperty; // The first ten global properties. globals = ((function() { __a = []; __b = window; for (name in __b) { - if (__b.hasOwnProperty(name)) { + if (__hasProp.call(__b, name)) { __a.push(name); } } diff --git a/documentation/js/long_arrow.js b/documentation/js/long_arrow.js new file mode 100644 index 00000000..8e60b143 --- /dev/null +++ b/documentation/js/long_arrow.js @@ -0,0 +1,20 @@ +(function(){ + var Account; + Account = function Account(customer, cart) { + var __a, __b; + var __this = this; + this.customer = customer; + this.cart = cart; + __a = $('.shopping_cart').bind('click', (function() { + __b = function(event) { + var __c; + __c = this.customer.purchase(this.cart); + return Account === this.constructor ? this : __c; + }; + return (function() { + return __b.apply(__this, arguments); + }); + })()); + return Account === this.constructor ? this : __a; + }; +})(); \ No newline at end of file diff --git a/documentation/js/object_comprehensions.js b/documentation/js/object_comprehensions.js index 81d94a41..9c7d3783 100644 --- a/documentation/js/object_comprehensions.js +++ b/documentation/js/object_comprehensions.js @@ -1,5 +1,6 @@ (function(){ var __a, __b, age, ages, child, years_old; + var __hasProp = Object.prototype.hasOwnProperty; years_old = { max: 10, ida: 9, @@ -9,7 +10,7 @@ __a = []; __b = years_old; for (child in __b) { age = __b[child]; - if (__b.hasOwnProperty(child)) { + if (__hasProp.call(__b, child)) { __a.push(child + " is " + age); } } diff --git a/documentation/js/overview.js b/documentation/js/overview.js index 1e61b371..2098e3a3 100644 --- a/documentation/js/overview.js +++ b/documentation/js/overview.js @@ -34,7 +34,7 @@ // Array comprehensions: cubed_list = (function() { __a = []; __b = list; - for (__c=0; __c<__b.length; __c++) { + for (__c = 0; __c < __b.length; __c++) { num = __b[__c]; __a.push(math.cube(num)); } diff --git a/documentation/js/switch.js b/documentation/js/switch.js index 53406faa..77e9a77d 100644 --- a/documentation/js/switch.js +++ b/documentation/js/switch.js @@ -1,14 +1,16 @@ (function(){ - if (day === "Tuesday") { - eat_breakfast(); - } else if (day === "Wednesday") { + if (day === "Mon") { + go_to_work(); + } else if (day === "Tue") { go_to_the_park(); - } else if (day === "Saturday") { + } else if (day === "Thu") { + go_ice_fishing(); + } else if (day === "Fri" || day === "Sat") { if (day === bingo_day) { go_to_bingo(); go_dancing(); } - } else if (day === "Sunday") { + } else if (day === "Sun") { go_to_church(); } else { go_to_work(); diff --git a/documentation/js/while.js b/documentation/js/while.js index cc835719..0ac87e11 100644 --- a/documentation/js/while.js +++ b/documentation/js/while.js @@ -1,9 +1,20 @@ (function(){ - while (demand > supply) { - sell(); - restock(); - } - while (supply > demand) { - buy(); + var __a, lyrics, num; + if (this.studying_economics) { + while (supply > demand) { + buy(); + } + while (supply < demand) { + sell(); + } } + num = 6; + lyrics = (function() { + __a = []; + while (num -= 1) { + __a.push(num + " little monkeys, jumping on the bed. \ +One fell out and bumped his head."); + } + return __a; + })(); })(); \ No newline at end of file diff --git a/index.html b/index.html index dd74befc..4a039662 100644 --- a/index.html +++ b/index.html @@ -37,7 +37,7 @@

Latest Version: - 0.2.4 + 0.2.5

Table of Contents

@@ -62,6 +62,7 @@ Inheritance, and Calling Super from a Subclass
Blocks
Pattern Matching
+ Function Binding
Embedded JavaScript
Switch/When/Else
Try/Catch/Finally
@@ -139,7 +140,7 @@ race = function // Array comprehensions: cubed_list = (function() { __a = []; __b = list; - for (__c=0; __c<__b.length; __c++) { + for (__c = 0; __c < __b.length; __c++) { num = __b[__c]; __a.push(math.cube(num)); } @@ -180,7 +181,7 @@ if ((typeof elvis !== "undefined" && elvis !== null)) { // Array comprehensions: cubed_list = (function() { __a = []; __b = list; - for (__c=0; __c<__b.length; __c++) { + for (__c = 0; __c < __b.length; __c++) { num = __b[__c]; __a.push(math.cube(num)); } @@ -667,21 +668,56 @@ backwards("stairway", "to", "heaven");

While Loops - The only low-level loop that CoffeeScript provides is the while loop. + The only low-level loop that CoffeeScript provides is the while loop. The + main difference from JavaScript is that the while loop can be used + as an expression, returning an array containing the result of each iteration + through the loop.

-
while demand > supply
-  sell()
-  restock()
+    
if this.studying_economics
+  while supply > demand then buy()
+  while supply < demand then sell()
 
-while supply > demand then buy()
-
while (demand > supply) {
-  sell();
-  restock();
+num: 6
+lyrics: while num -= 1
+  num + " little monkeys, jumping on the bed.
+    One fell out and bumped his head."
+
var __a, lyrics, num;
+if (this.studying_economics) {
+  while (supply > demand) {
+    buy();
+  }
+  while (supply < demand) {
+    sell();
+  }
 }
-while (supply > demand) {
-  buy();
+num = 6;
+lyrics = (function() {
+  __a = [];
+  while (num -= 1) {
+    __a.push(num + " little monkeys, jumping on the bed. \
+One fell out and bumped his head.");
+  }
+  return __a;
+})();
+
+num = 6; +lyrics = (function() { + __a = []; + while (num -= 1) { + __a.push(num + " little monkeys, jumping on the bed. \ +One fell out and bumped his head."); + } + return __a; +})(); +;alert(lyrics.join("\n"));'>run: lyrics.join("\n")

Other JavaScript loops, such as for loops and do-while loops can be mimicked by variations on while, but the hope is that you @@ -709,7 +745,7 @@ backwards("stairway", "to", "heaven"); // Eat lunch. lunch = (function() { __a = []; __b = ['toast', 'cheese', 'wine']; - for (__c=0; __c<__b.length; __c++) { + for (__c = 0; __c < __b.length; __c++) { food = __b[__c]; __a.push(eat(food)); } @@ -717,10 +753,10 @@ lunch = (function() { })(); // Naive collision detection. __d = asteroids; -for (__e=0; __e<__d.length; __e++) { +for (__e = 0; __e < __d.length; __e++) { roid = __d[__e]; __f = asteroids; - for (__g=0; __g<__f.length; __g++) { + for (__g = 0; __g < __f.length; __g++) { roid2 = __f[__g]; if (roid !== roid2) { if (roid.overlaps(roid2)) { @@ -791,6 +827,7 @@ egg_delivery = function egg_delivery() { ages: for child, age of years_old child + " is " + age

var __a, __b, age, ages, child, years_old;
+var __hasProp = Object.prototype.hasOwnProperty;
 years_old = {
   max: 10,
   ida: 9,
@@ -800,13 +837,14 @@ ages = (function() {
   __a = []; __b = years_old;
   for (child in __b) {
     age = __b[child];
-    if (__b.hasOwnProperty(child)) {
+    if (__hasProp.call(__b, child)) {
       __a.push(child + " is " + age);
     }
   }
   return __a;
 })();
 

+

+ Function binding + The long arrow ==> can be used to both define a function, and to bind + it to the current value of this, right on the spot. This is helpful + when using callback-based libraries like Prototype or jQuery, for creating + iterator functions to pass to each, or event-handler functions + to use with bind. Functions created with the long arrow are able to access + properties of the this where they're defined. +

+
Account: customer, cart =>
+  this.customer: customer
+  this.cart: cart
+
+  $('.shopping_cart').bind('click') event ==>
+    this.customer.purchase(this.cart)
+
var Account;
+Account = function Account(customer, cart) {
+  var __a, __b;
+  var __this = this;
+  this.customer = customer;
+  this.cart = cart;
+  __a = $('.shopping_cart').bind('click', (function() {
+    __b = function(event) {
+      var __c;
+      __c = this.customer.purchase(this.cart);
+      return Account === this.constructor ? this : __c;
+    };
+    return (function() {
+      return __b.apply(__this, arguments);
+    });
+  })());
+  return Account === this.constructor ? this : __a;
+};
+

+

Embedded JavaScript Hopefully, you'll never need to use it, but if you ever need to intersperse @@ -1261,25 +1336,33 @@ return [document.title, "Hello JavaScript"].join(": "); in a returnable, assignable expression. The format is: switch condition, when clauses, else the default case.

+

+ As in Ruby, switch statements in CoffeeScript can take multiple + values for each when clause. If any of the values match, the clause + runs. +

switch day
-  when "Tuesday"   then eat_breakfast()
-  when "Wednesday" then go_to_the_park()
-  when "Saturday"
+  when "Mon" then go_to_work()
+  when "Tue" then go_to_the_park()
+  when "Thu" then go_ice_fishing()
+  when "Fri", "Sat"
     if day is bingo_day
       go_to_bingo()
       go_dancing()
-  when "Sunday"    then go_to_church()
+  when "Sun" then go_to_church()
   else go_to_work()
-
if (day === "Tuesday") {
-  eat_breakfast();
-} else if (day === "Wednesday") {
+
if (day === "Mon") {
+  go_to_work();
+} else if (day === "Tue") {
   go_to_the_park();
-} else if (day === "Saturday") {
+} else if (day === "Thu") {
+  go_ice_fishing();
+} else if (day === "Fri" || day === "Sat") {
   if (day === bingo_day) {
     go_to_bingo();
     go_dancing();
   }
-} else if (day === "Sunday") {
+} else if (day === "Sun") {
   go_to_church();
 } else {
   go_to_work();
@@ -1406,7 +1489,16 @@ html = &q
     
 
     

Change Log

- + +

+ 0.2.5 + The conditions in switch statements can now take multiple values at once — + If any of them are true, the case will run. Added the long arrow ==>, + which defines and immediately binds a function to this. While loops can + now be used as expressions, in the same way that comprehensions can. Splats + can be used within pattern matches to soak up the rest of an array. +

+

0.2.4 Added ECMAScript Harmony style destructuring assignment, for dealing with diff --git a/lib/coffee-script.rb b/lib/coffee-script.rb index 3fc2d6ad..8e92bc9f 100644 --- a/lib/coffee-script.rb +++ b/lib/coffee-script.rb @@ -10,7 +10,7 @@ require "coffee_script/parse_error" # Namespace for all CoffeeScript internal classes. module CoffeeScript - VERSION = '0.2.4' # Keep in sync with the gemspec. + VERSION = '0.2.5' # Keep in sync with the gemspec. # Compile a script (String or IO) to JavaScript. def self.compile(script, options={}) diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 1667e299..b27fb725 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -34,11 +34,12 @@ module CoffeeScript ASSIGNMENT = /\A(:|=)\Z/ # Token cleaning regexes. - JS_CLEANER = /(\A`|`\Z)/ - MULTILINER = /\n/ + JS_CLEANER = /(\A`|`\Z)/ + MULTILINER = /\n/ + STRING_NEWLINES = /\n\s*/ COMMENT_CLEANER = /(^\s*#|\n\s*$)/ - NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/ - HEREDOC_INDENT = /^\s+/ + NO_NEWLINE = /\A([+\*&|\/\-%=<>:!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)\Z/ + HEREDOC_INDENT = /^\s+/ # Tokens which a regular expression will never immediately follow, but which # a division operator might. @@ -106,7 +107,7 @@ module CoffeeScript # Matches strings, including multi-line strings. def string_token return false unless string = @chunk[STRING, 1] - escaped = string.gsub(MULTILINER, " \\\n") + escaped = string.gsub(STRING_NEWLINES, " \\\n") token(:STRING, escaped) @line += string.count("\n") @i += string.length diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 234560da..2ea59058 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -750,7 +750,7 @@ module CoffeeScript else index_var = nil source_part = "#{svar} = #{source.compile(o)};\n#{idt}" - for_part = @object ? "#{ivar} in #{svar}" : "#{ivar}=0; #{ivar}<#{svar}.length; #{ivar}++" + for_part = @object ? "#{ivar} in #{svar}" : "#{ivar} = 0; #{ivar} < #{svar}.length; #{ivar}++" var_part = @name ? "#{body_dent}#{@name} = #{svar}[#{ivar}];\n" : '' end body = @body diff --git a/package.json b/package.json index 0d1478fa..3c865628 100644 --- a/package.json +++ b/package.json @@ -5,5 +5,5 @@ "description": "Unfancy JavaScript", "keywords": ["javascript", "language"], "author": "Jeremy Ashkenas", - "version": "0.2.4" + "version": "0.2.5" }