diff --git a/Rakefile b/Rakefile index 0616f0fc..497d07e8 100644 --- a/Rakefile +++ b/Rakefile @@ -18,6 +18,7 @@ end desc "Build the documentation page" task :doc do source = 'documentation/index.html.erb' + Thread.new { `bin/coffee-script documentation/cs/*.cs -o documentation/js -w` } loop do mtime = File.stat(source).mtime if !@mtime || mtime > @mtime diff --git a/documentation/cs/array_comprehensions.cs b/documentation/cs/array_comprehensions.cs new file mode 100644 index 00000000..ffccbfee --- /dev/null +++ b/documentation/cs/array_comprehensions.cs @@ -0,0 +1,5 @@ +# Eat lunch. +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. diff --git a/documentation/cs/assignment.cs b/documentation/cs/assignment.cs new file mode 100644 index 00000000..db979f5c --- /dev/null +++ b/documentation/cs/assignment.cs @@ -0,0 +1,2 @@ +greeting: "Hello CoffeeScript" +difficulty: 0.5 diff --git a/documentation/cs/conditionals.cs b/documentation/cs/conditionals.cs new file mode 100644 index 00000000..253d912c --- /dev/null +++ b/documentation/cs/conditionals.cs @@ -0,0 +1,9 @@ +mood: greatly_improved if singing + +if happy and knows_it + claps_hands() + cha_cha_cha(). + +date: if friday then sue else jill. + +expensive ||= do_the_math() \ No newline at end of file diff --git a/documentation/cs/embedded.cs b/documentation/cs/embedded.cs new file mode 100644 index 00000000..5a4f0d24 --- /dev/null +++ b/documentation/cs/embedded.cs @@ -0,0 +1,3 @@ +js: => `alert("Hello JavaScript");`. + +js() if 10 > 9 \ No newline at end of file diff --git a/documentation/cs/expressions.cs b/documentation/cs/expressions.cs new file mode 100644 index 00000000..3309739a --- /dev/null +++ b/documentation/cs/expressions.cs @@ -0,0 +1,9 @@ +grade: student => + if student.excellent_work + "A+" + else if student.okay_stuff + "B" + else + "C".. + +eldest: if 24 > 21 then "Liz" else "Ike". \ No newline at end of file diff --git a/documentation/cs/functions.cs b/documentation/cs/functions.cs new file mode 100644 index 00000000..35f4415a --- /dev/null +++ b/documentation/cs/functions.cs @@ -0,0 +1,2 @@ +square: x => x * x. +cube: x => square(x) * x. \ No newline at end of file diff --git a/documentation/cs/intro.cs b/documentation/cs/intro.cs new file mode 100644 index 00000000..b0f13bb9 --- /dev/null +++ b/documentation/cs/intro.cs @@ -0,0 +1,3 @@ +# CoffeeScript on the left, JS on the right. + +square: x => x * x. diff --git a/documentation/cs/objects_and_arrays.cs b/documentation/cs/objects_and_arrays.cs new file mode 100644 index 00000000..ff10c6ed --- /dev/null +++ b/documentation/cs/objects_and_arrays.cs @@ -0,0 +1,6 @@ +song: ["do", "re", "mi", "fa", "so"] +ages: { + max: 10 + ida: 9 + tim: 11 +} \ No newline at end of file diff --git a/documentation/cs/punctuation.cs b/documentation/cs/punctuation.cs new file mode 100644 index 00000000..3ecf2073 --- /dev/null +++ b/documentation/cs/punctuation.cs @@ -0,0 +1,11 @@ +# Comments start with hash marks. + +# Periods mark the end of a block. +left_hand: if raining then umbrella else parasol. + +# To signal the beginning of the next expression, +# use "then", or a newline. +left_hand: if raining + umbrella +else + parasol. diff --git a/documentation/cs/scope.cs b/documentation/cs/scope.cs new file mode 100644 index 00000000..f15b3eff --- /dev/null +++ b/documentation/cs/scope.cs @@ -0,0 +1,5 @@ +num: 1 +change_numbers: => + num: 2 + new_num: 3. +new_num: change_numbers() \ No newline at end of file diff --git a/documentation/cs/slices.cs b/documentation/cs/slices.cs new file mode 100644 index 00000000..ac34b0b2 --- /dev/null +++ b/documentation/cs/slices.cs @@ -0,0 +1,2 @@ +nums: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +three_to_six: nums[3, 6] \ No newline at end of file diff --git a/documentation/cs/strings.cs b/documentation/cs/strings.cs new file mode 100644 index 00000000..335d702b --- /dev/null +++ b/documentation/cs/strings.cs @@ -0,0 +1,6 @@ +moby_dick: "Call me Ishmael. Some years ago -- +never mind how long precisely -- having little +or no money in my purse, and nothing particular +to interest me on shore, I thought I would sail +about a little and see the watery part of the +world..." \ No newline at end of file diff --git a/documentation/cs/super.cs b/documentation/cs/super.cs new file mode 100644 index 00000000..21c89a26 --- /dev/null +++ b/documentation/cs/super.cs @@ -0,0 +1,21 @@ +Animal: => . +Animal.prototype.move: meters => + alert(this.name + " moved " + meters + "m."). + +Snake: name => this.name: name. +Snake extends new Animal() +Snake.prototype.move: => + alert("Slithering...") + super(5). + +Horse: name => this.name: name. +Horse extends new Animal() +Horse.prototype.move: => + alert("Galloping...") + super(45). + +sam: new Snake("Sammy the Python") +tom: new Horse("Tommy the Palomino") + +sam.move() +tom.move() diff --git a/documentation/cs/switch.cs b/documentation/cs/switch.cs new file mode 100644 index 00000000..41bf9045 --- /dev/null +++ b/documentation/cs/switch.cs @@ -0,0 +1,7 @@ +switch day +case "Tuesday" then eat_breakfast() +case "Wednesday" then go_to_the_park() +case "Saturday" + if day is bingo_day then go_to_bingo(). +case "Sunday" then go_to_church() +else go_to_work(). \ No newline at end of file diff --git a/documentation/cs/try.cs b/documentation/cs/try.cs new file mode 100644 index 00000000..6664bbd1 --- /dev/null +++ b/documentation/cs/try.cs @@ -0,0 +1,7 @@ +try + all_hell_breaks_loose() + cats_and_dogs_living_together() +catch error + print( error ) +finally + clean_up(). \ No newline at end of file diff --git a/documentation/cs/while.cs b/documentation/cs/while.cs new file mode 100644 index 00000000..ab0a850a --- /dev/null +++ b/documentation/cs/while.cs @@ -0,0 +1,5 @@ +while demand > supply + sell() + restock(). + +while supply > demand then buy(). \ No newline at end of file diff --git a/documentation/css/docs.css b/documentation/css/docs.css index f50ad3b1..cd45d0a8 100644 --- a/documentation/css/docs.css +++ b/documentation/css/docs.css @@ -1,40 +1,34 @@ body { - font-size: 16px; - line-height: 24px; - background: #f0f0e5; - color: #252519; - font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; + font-size: 14px; + line-height: 20px; + background: #efefef; + color: #191933; + font-family: Arial, Helvetica, sans-serif; } div.container { - width: 720px; + width: 850px; margin: 50px 0 50px 50px; } p { - width: 550px; + padding-left: 13px; + width: 625px; } - #documentation p { - margin-bottom: 4px; - } -a, a:visited { - padding: 0 2px; - text-decoration: none; - background: #dadaba; - color: #252519; -} -a:active, a:hover { - color: #000; - background: #f0c095; +a { + color: #000055; } h1, h2, h3, h4, h5, h6 { + padding-left: 13px; margin-top: 40px; } -b.header { - font-size: 18px; +br.clear { + height: 0; + clear: both; } -span.alias { - font-size: 14px; - font-style: italic; - margin-left: 20px; +b.header { + color: #000055; + display: block; + margin: 40px 0 5px 0; + font-size: 16px; } table, tr, td { margin: 0; padding: 0; @@ -53,7 +47,24 @@ code, pre, tt { } pre { font-size: 12px; - padding: 2px 0 2px 12px; - border-left: 6px solid #aaaa99; - margin: 0px 0 30px; + padding: 0 0 0 12px; + margin: 0; + width: 410px; + float: left; + border-left: 1px dotted #559; + } + pre:first-child { + border-left: 0; + } +div.code { + position: relative; + border: 1px solid #cacaca; + background: #fff; + padding: 7px 0 10px 0; + -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; + -webkit-box-shadow: 0px 0px 7px #cacaca; +} + div.code button { + position: absolute; + right: 8px; bottom: 8px; } \ No newline at end of file diff --git a/documentation/index.html.erb b/documentation/index.html.erb index 5ea6ffde..d571d13a 100644 --- a/documentation/index.html.erb +++ b/documentation/index.html.erb @@ -1,26 +1,222 @@ +<% + require 'uv' + def code_for(file, executable=false) + @stripper ||= /(\A\(function\(\)\{\n|\}\)\(\);\Z|^ )/ + cs = File.read("documentation/cs/#{file}.cs") + js = File.read("documentation/js/#{file}.js").gsub(@stripper, '') + cshtml = Uv.parse(cs, 'xhtml', 'coffeescript', false, 'idle', false) + jshtml = Uv.parse(js, 'xhtml', 'javascript', false, 'idle', false) + append = executable == true ? '' : "alert(#{executable});" + run = executable == true ? 'run' : "run: #{executable}" + button = executable ? "" : '' + "
#{cshtml}#{jshtml}#{button}
" + end +%> + + - CoffeeScript - + CoffeeScript, briefly... + +
-

CoffeeScript

- +

CoffeeScript

+

CoffeeScript is a little language that compiles into JavaScript. Think of it as JavaScript's simpleminded kid brother — the same genes, - the same accent, but another kind of way of doing things. + the same accent, but a different sense of style. Apart from a handful of + bonus goodies, statements in CoffeeScript correspond one-to-one with their + JavaScript equivalent, it's just another way of saying it.

- + + +

- + Disclaimer:
+ CoffeeScript is just for fun and seriously alpha. There is no guarantee, + explicit or implied, of its suitability for any purpose. That said, it + compiles into pretty-printed JavaScript (the good parts) that can pass through + JSLint warning-free.

+

Table of Contents

+ +

+ + This document is structured so that it can be read from top to bottom, + if you like. Later sections use ideas and syntax previously introduced. + +

+ +

+ Punctuation Primer
+ Functions and Invocation
+ Objects and Arrays
+ Assignment
+ Lexical Scoping and Variable Safety
+ Conditionals, Ternaries, and Conditional Assignment
+ Everything is an Expression
+ While Loops
+ Array Comprehensions
+ Array Slice Literals
+ Inheritance, and Calling Super from a Subclass + Embedded JavaScript
+ Switch/Case/Else
+ Try/Catch/Finally
+ Multiline Strings
+

+ +

+ In all of the following examples, the source CoffeeScript is provided on + the left, and the direct compilation into JavaScript is on the right. +

+ +
+ +

+ Punctuation Primer + You don't need to use semicolons to (;) terminate expressions, ending + the line will do just as well. So newlines can matter, but whitespace is + not otherwise significant. Instead of using curly braces ({ }) + to delimit blocks of code, a period (.) marks the end of a + function, if statement, or try/catch. +

+ + +

+ Functions and Invocation + Let's start with the best part, shall we? Function literals are my + absolute favorite thing about CoffeeScript. +

+ <%= code_for('functions', 'cube(5)') %> + +

+ Objects and Arrays + Object and Array literals look very similar. When you spread out + each assignment on a separate line, the commas are optional. +

+ <%= code_for('objects_and_arrays', 'song.join(",")') %> +

+

+ +

+ Assignment + All assignment in CoffeeScript, whether to a variable or to an object + property, uses a colon. Equal signs are only needed for mathy things. +

+ <%= code_for('assignment', 'greeting') %> +

+

+ +

+ Lexical Scoping and Variable Safety + The CoffeeScript compiler takes care to make sure that all of your variables + are properly defined within lexical scope — you never need to declare + var yourself. +

+ <%= code_for('scope', 'new_num') %> +

+ Notice how the variables are declared with var the first time + they appear. The second reference of num, within the function, + is not redeclared because num is still in scope. As opposed + to the second new_num, in the last line. +

+ +

+ Conditionals, Ternaries, and Conditional Assignment +

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

+

+ +

+ Everything is an Expression + You might have noticed how even though we don't add return statements + to CoffeScript functions, they nonetheless return their final value. + The CoffeeScript compiler tries to make sure that every little language + construct can be used as an expression. +

+ <%= code_for('expressions', 'eldest') %> +

+ When compiling a function definition, CoffeeScript tries to push down + the return statement to each of the potential final lines of the function. + It uses the same mechanism to push down assignment statements. If statement + are compiled into ternary operators when possible, so that they can be used + as expressions. +

+ +

+ While Loops + The only low-level loop that CoffeeScript provides is the while loop. +

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

+ Array Comprehensions + Most of your looping needs should be handled by array comprehensions. + They replace (and compile into) for loops, handling + each/forEach style loops, as well as select/filter. + Unlike for loops, array comprehensions are expressions, and can be returned + and assigned. +

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

+ Array Slice Literals + CoffeeScript includes a literal syntax for extracting slices of arrays. + The first argument is the index of the first element in the slice, and + the second is the index of the last one. +

+ <%= code_for('slices', 'three_to_six') %> + +

+ Inheritance, and Calling Super from a Subclass + JavaScript's prototypal inheritance has always been a bit of a + brain-bender, with a whole family tree of libraries (Base2, Prototype + ). +

+ <%= code_for('super', true) %> + +

+ Embedded JavaScript + If you ever need to interpolate literal JavaScript snippets, you can + use backticks to pass JavaScript straight through. +

+ <%= code_for('embedded', true) %> + +

+ Switch/Case/Else + Switch statements in JavaScript are fundamentally broken. You can only + do string comparisons, and need to break at the end of each case + statment to prevent falling through to the default case. CoffeeScript + compiles switch statements into if-else chains, allowing you to + compare any object (via ===), preventing fall-through, and resulting + in a returnable expression. +

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

+ Try/Catch/Finally + Try/catch statements just about the same as JavaScript (although + they work as expressions). No braces required. +

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

+ Multiline Strings + Multiline strings are allowed in CoffeeScript. +

+ <%= code_for('strings') %> + +
+
diff --git a/documentation/js/array_comprehensions.js b/documentation/js/array_comprehensions.js new file mode 100644 index 00000000..75242df7 --- /dev/null +++ b/documentation/js/array_comprehensions.js @@ -0,0 +1,16 @@ +(function(){ + var lunch; + var a = ['toast', 'cheese', 'wine']; + var d = []; + for (var b=0, c=a.length; b 9) { + js(); + } +})(); \ No newline at end of file diff --git a/documentation/js/expressions.js b/documentation/js/expressions.js new file mode 100644 index 00000000..b6287487 --- /dev/null +++ b/documentation/js/expressions.js @@ -0,0 +1,12 @@ +(function(){ + var grade = function(student) { + if (student.excellent_work) { + return "A+"; + } else if (student.okay_stuff) { + return "B"; + } else { + return "C"; + } + }; + var eldest = 24 > 21 ? "Liz" : "Ike"; +})(); \ No newline at end of file diff --git a/documentation/js/expressions_assignment.js b/documentation/js/expressions_assignment.js new file mode 100644 index 00000000..97783f1b --- /dev/null +++ b/documentation/js/expressions_assignment.js @@ -0,0 +1,3 @@ +(function(){ + +})(); \ No newline at end of file diff --git a/documentation/js/functions.js b/documentation/js/functions.js new file mode 100644 index 00000000..2fc0d2f8 --- /dev/null +++ b/documentation/js/functions.js @@ -0,0 +1,8 @@ +(function(){ + var square = function(x) { + return x * x; + }; + var cube = function(x) { + return square(x) * x; + }; +})(); \ No newline at end of file diff --git a/documentation/js/intro.js b/documentation/js/intro.js new file mode 100644 index 00000000..7d544a25 --- /dev/null +++ b/documentation/js/intro.js @@ -0,0 +1,5 @@ +(function(){ + var square = function(x) { + return x * x; + }; +})(); \ No newline at end of file diff --git a/documentation/js/objects_and_arrays.js b/documentation/js/objects_and_arrays.js new file mode 100644 index 00000000..bd3276cc --- /dev/null +++ b/documentation/js/objects_and_arrays.js @@ -0,0 +1,8 @@ +(function(){ + var song = ["do", "re", "mi", "fa", "so"]; + var ages = { + max: 10, + ida: 9, + tim: 11 + }; +})(); \ No newline at end of file diff --git a/documentation/js/punctuation.js b/documentation/js/punctuation.js new file mode 100644 index 00000000..5ac63fdb --- /dev/null +++ b/documentation/js/punctuation.js @@ -0,0 +1,4 @@ +(function(){ + var left_hand = raining ? umbrella : parasol; + left_hand = raining ? umbrella : parasol; +})(); \ No newline at end of file diff --git a/documentation/js/scope.js b/documentation/js/scope.js new file mode 100644 index 00000000..1f110baa --- /dev/null +++ b/documentation/js/scope.js @@ -0,0 +1,9 @@ +(function(){ + var num = 1; + var change_numbers = function() { + num = 2; + var new_num = 3; + return new_num; + }; + var new_num = change_numbers(); +})(); \ No newline at end of file diff --git a/documentation/js/slices.js b/documentation/js/slices.js new file mode 100644 index 00000000..a82ecdbe --- /dev/null +++ b/documentation/js/slices.js @@ -0,0 +1,4 @@ +(function(){ + var nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + var three_to_six = nums.slice(3, 6 + 1); +})(); \ No newline at end of file diff --git a/documentation/js/strings.js b/documentation/js/strings.js new file mode 100644 index 00000000..37876208 --- /dev/null +++ b/documentation/js/strings.js @@ -0,0 +1,8 @@ +(function(){ + var moby_dick = "Call me Ishmael. Some years ago --\ +never mind how long precisely -- having little\ +or no money in my purse, and nothing particular\ +to interest me on shore, I thought I would sail\ +about a little and see the watery part of the\ +world..."; +})(); \ No newline at end of file diff --git a/documentation/js/super.js b/documentation/js/super.js new file mode 100644 index 00000000..8989fafd --- /dev/null +++ b/documentation/js/super.js @@ -0,0 +1,28 @@ +(function(){ + var Animal = function() { + + }; + Animal.prototype.move = function(meters) { + return alert(this.name + " moved " + meters + "m."); + }; + var Snake = function(name) { + this.name = name; + }; + Snake.prototype = new Animal(); + Snake.prototype.move = function() { + alert("Slithering..."); + return this.constructor.prototype.move.call(this, 5); + }; + var Horse = function(name) { + this.name = name; + }; + Horse.prototype = new Animal(); + Horse.prototype.move = function() { + alert("Galloping..."); + return this.constructor.prototype.move.call(this, 45); + }; + var sam = new Snake("Sammy the Python"); + var tom = new Horse("Tommy the Palomino"); + sam.move(); + tom.move(); +})(); \ No newline at end of file diff --git a/documentation/js/switch.js b/documentation/js/switch.js new file mode 100644 index 00000000..36a2a15e --- /dev/null +++ b/documentation/js/switch.js @@ -0,0 +1,13 @@ +(function(){ + if (day === "Tuesday") { + eat_breakfast(); + } else if (day === "Wednesday") { + go_to_the_park(); + } else if (day === "Saturday") { + day === bingo_day ? go_to_bingo() : null; + } else if (day === "Sunday") { + go_to_church(); + } else { + go_to_work(); + } +})(); \ No newline at end of file diff --git a/documentation/js/try.js b/documentation/js/try.js new file mode 100644 index 00000000..a2995e6d --- /dev/null +++ b/documentation/js/try.js @@ -0,0 +1,10 @@ +(function(){ + try { + all_hell_breaks_loose(); + cats_and_dogs_living_together(); + } catch (error) { + print(error); + } finally { + clean_up(); + } +})(); \ No newline at end of file diff --git a/documentation/js/while.js b/documentation/js/while.js new file mode 100644 index 00000000..cc835719 --- /dev/null +++ b/documentation/js/while.js @@ -0,0 +1,9 @@ +(function(){ + while (demand > supply) { + sell(); + restock(); + } + while (supply > demand) { + buy(); + } +})(); \ No newline at end of file diff --git a/examples/code.cs b/examples/code.cs index a06dbb7e..6f533fce 100644 --- a/examples/code.cs +++ b/examples/code.cs @@ -136,13 +136,28 @@ story: "Lorem ipsum dolor \"sit\" amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad." -# Calling super from an overridden method. -Greeter: => . # Create the parent object. -Greeter.prototype.hello: name => alert('Hello ' + name). # Define a "hello" method. -Exclaimer: name => this.name: name. # Create the child object. -Exclaimer.prototype: new Greeter() # Set the child to inherit from the parent. -Exclaimer.prototype.hello: => super(this.name + "!"). # The child's "hello" calls the parent's via "super". -(new Exclaimer('Bob')).hello() # Run it. +# Inheritance and calling super. +Animal: => . +Animal.prototype.move: meters => + alert(this.name + " moved " + meters + "m."). + +Snake: name => this.name: name. +Snake extends Animal +Snake.prototype.move: => + alert('Slithering...') + super(5). + +Horse: name => this.name: name. +Horse extends Animal +Horse.prototype.move: => + alert('Galloping...') + super(45). + +sam: new Snake("Sammy the Snake") +tom: new Horse("Tommy the Horse") + +sam.move() +tom.move() # Numbers. a_googol: 1e100 diff --git a/index.html b/index.html index cb032b44..11480003 100644 --- a/index.html +++ b/index.html @@ -1,22 +1,484 @@ + + + - CoffeeScript - + CoffeeScript, briefly... + +
-

CoffeeScript

- +

CoffeeScript

+

CoffeeScript is a little language that compiles into JavaScript. Think of it as JavaScript's simpleminded kid brother — the same genes, - the same accent, but another kind of way of doing things. + the same accent, but a different sense of style. Apart from a handful of + bonus goodies, statements in CoffeeScript correspond one-to-one with their + JavaScript equivalent, it's just another way of saying it.

+ + +

+ Disclaimer:
+ CoffeeScript is just for fun and seriously alpha. There is no guarantee, + explicit or implied, of its suitability for any purpose. That said, it + compiles into pretty-printed JavaScript (the good parts) that can pass through + JSLint warning-free. +

+ +

Table of Contents

+ +

+ + This document is structured so that it can be read from top to bottom, + if you like. Later sections use ideas and syntax previously introduced. + +

+ +

+ Punctuation Primer
+ Functions and Invocation
+ Objects and Arrays
+ Assignment
+ Lexical Scoping and Variable Safety
+ Conditionals, Ternaries, and Conditional Assignment
+ Everything is an Expression
+ While Loops
+ Array Comprehensions
+ Array Slice Literals
+ Inheritance, and Calling Super from a Subclass + Embedded JavaScript
+ Switch/Case/Else
+ Try/Catch/Finally
+ Multiline Strings
+

+ +

+ In all of the following examples, the source CoffeeScript is provided on + the left, and the direct compilation into JavaScript is on the right. +

+ +
+ +

+ Punctuation Primer + You don't need to use semicolons to (;) terminate expressions, ending + the line will do just as well. So newlines can matter, but whitespace is + not otherwise significant. Instead of using curly braces ({ }) + to delimit blocks of code, a period (.) marks the end of a + function, if statement, or try/catch. +

+ + +

+ Functions and Invocation + Let's start with the best part, shall we? Function literals are my + absolute favorite thing about CoffeeScript. +

+
square: x => x * x.
+cube:   x => square(x) * x.
+
var square = function(x) {
+  return x * x;
+};
+var cube = function(x) {
+  return square(x) * x;
+};
+

+ +

+ Objects and Arrays + Object and Array literals look very similar. When you spread out + each assignment on a separate line, the commas are optional. +

+
song: ["do", "re", "mi", "fa", "so"]
+ages: {
+  max: 10
+  ida: 9
+  tim: 11
+}
+
var song = ["do", "re", "mi", "fa", "so"];
+var ages = {
+  max: 10,
+  ida: 9,
+  tim: 11
+};
+

+

+

+ +

+ Assignment + All assignment in CoffeeScript, whether to a variable or to an object + property, uses a colon. Equal signs are only needed for mathy things. +

+
greeting: "Hello CoffeeScript"
+difficulty: 0.5
+
var greeting = "Hello CoffeeScript";
+var difficulty = 0.5;
+

+

+

+ +

+ Lexical Scoping and Variable Safety + The CoffeeScript compiler takes care to make sure that all of your variables + are properly defined within lexical scope — you never need to declare + var yourself. +

+
num: 1
+change_numbers: =>
+  num: 2
+  new_num: 3.
+new_num: change_numbers()
+
var num = 1;
+var change_numbers = function() {
+  num = 2;
+  var new_num = 3;
+  return new_num;
+};
+var new_num = change_numbers();
+

+

+ Notice how the variables are declared with var the first time + they appear. The second reference of num, within the function, + is not redeclared because num is still in scope. As opposed + to the second new_num, in the last line. +

+ +

+ Conditionals, Ternaries, and Conditional Assignment +

+
mood: greatly_improved if singing
+
+if happy and knows_it
+  claps_hands()
+  cha_cha_cha().
+
+date: if friday then sue else jill.
+
+expensive ||= do_the_math()
+
var mood;
+if (singing) {
+  mood = greatly_improved;
+}
+if (happy && knows_it) {
+  claps_hands();
+  cha_cha_cha();
+}
+var date = friday ? sue : jill;
+expensive = expensive || do_the_math();
+

+

+

+ +

+ Everything is an Expression + You might have noticed how even though we don't add return statements + to CoffeScript functions, they nonetheless return their final value. + The CoffeeScript compiler tries to make sure that every little language + construct can be used as an expression. +

+
grade: student =>
+  if student.excellent_work
+    "A+"
+  else if student.okay_stuff
+    "B"
+  else
+    "C"..
+
+eldest: if 24 > 21 then "Liz" else "Ike".
+
var grade = function(student) {
+  if (student.excellent_work) {
+    return "A+";
+  } else if (student.okay_stuff) {
+    return "B";
+  } else {
+    return "C";
+  }
+};
+var eldest = 24 > 21 ? "Liz" : "Ike";
+

+

+ When compiling a function definition, CoffeeScript tries to push down + the return statement to each of the potential final lines of the function. + It uses the same mechanism to push down assignment statements. If statement + are compiled into ternary operators when possible, so that they can be used + as expressions. +

+ +

+ While Loops + The only low-level loop that CoffeeScript provides is the while loop. +

+
while demand > supply
+  sell()
+  restock().
+
+while supply > demand then buy().
+
while (demand > supply) {
+  sell();
+  restock();
+}
+while (supply > demand) {
+  buy();
+}
+

+ +

+ Array Comprehensions + Most of your looping needs should be handled by array comprehensions. + They replace (and compile into) for loops, handling + each/forEach style loops, as well as select/filter. + Unlike for loops, array comprehensions are expressions, and can be returned + and assigned. +

+
# Eat lunch.
+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.
+
var lunch;
+var a = ['toast', 'cheese', 'wine'];
+var d = [];
+for (var b=0, c=a.length; b<c; b++) {
+  var food = a[b];
+  d[b] = food.eat();
+}
+lunch = d;
+var e = table;
+for (var f=0, g=e.length; f<g; f++) {
+  var row = e[f];
+  var i = f;
+  i % 2 === 0 ? highlight(row) : null;
+}
+

+ +

+ Array Slice Literals + CoffeeScript includes a literal syntax for extracting slices of arrays. + The first argument is the index of the first element in the slice, and + the second is the index of the last one. +

+
nums: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+three_to_six: nums[3, 6]
+
var nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+var three_to_six = nums.slice(3, 6 + 1);
+

+ +

+ Inheritance, and Calling Super from a Subclass + JavaScript's prototypal inheritance has always been a bit of a + brain-bender, with a whole family tree of libraries (Base2, Prototype + ). +

+
Animal: => .
+Animal.prototype.move: meters =>
+  alert(this.name + " moved " + meters + "m.").
+
+Snake: name => this.name: name.
+Snake extends new Animal()
+Snake.prototype.move: =>
+  alert("Slithering...")
+  super(5).
+
+Horse: name => this.name: name.
+Horse extends new Animal()
+Horse.prototype.move: =>
+  alert("Galloping...")
+  super(45).
+
+sam: new Snake("Sammy the Python")
+tom: new Horse("Tommy the Palomino")
+
+sam.move()
+tom.move()
+
var Animal = function() {
+
+};
+Animal.prototype.move = function(meters) {
+  return alert(this.name + " moved " + meters + "m.");
+};
+var Snake = function(name) {
+  this.name = name;
+};
+Snake.prototype = new Animal();
+Snake.prototype.move = function() {
+  alert("Slithering...");
+  return this.constructor.prototype.move.call(this, 5);
+};
+var Horse = function(name) {
+  this.name = name;
+};
+Horse.prototype = new Animal();
+Horse.prototype.move = function() {
+  alert("Galloping...");
+  return this.constructor.prototype.move.call(this, 45);
+};
+var sam = new Snake("Sammy the Python");
+var tom = new Horse("Tommy the Palomino");
+sam.move();
+tom.move();
+

+ +

+ Embedded JavaScript + If you ever need to interpolate literal JavaScript snippets, you can + use backticks to pass JavaScript straight through. +

+
js: => `alert("Hello JavaScript");`.
+
+js() if 10 > 9
+
var js = function() {
+  return alert("Hello JavaScript");
+};
+if (10 > 9) {
+  js();
+}
+

+ +

+ Switch/Case/Else + Switch statements in JavaScript are fundamentally broken. You can only + do string comparisons, and need to break at the end of each case + statment to prevent falling through to the default case. CoffeeScript + compiles switch statements into if-else chains, allowing you to + compare any object (via ===), preventing fall-through, and resulting + in a returnable expression. +

+
switch day
+case "Tuesday"   then eat_breakfast()
+case "Wednesday" then go_to_the_park()
+case "Saturday"
+  if day is bingo_day then go_to_bingo().
+case "Sunday"    then go_to_church()
+else go_to_work().
+
if (day === "Tuesday") {
+  eat_breakfast();
+} else if (day === "Wednesday") {
+  go_to_the_park();
+} else if (day === "Saturday") {
+  day === bingo_day ? go_to_bingo() : null;
+} else if (day === "Sunday") {
+  go_to_church();
+} else {
+  go_to_work();
+}
+

+ +

+ Try/Catch/Finally + Try/catch statements just about the same as JavaScript (although + they work as expressions). No braces required. +

+
try
+  all_hell_breaks_loose()
+  cats_and_dogs_living_together()
+catch error
+  print( error )
+finally
+  clean_up().
+
try {
+  all_hell_breaks_loose();
+  cats_and_dogs_living_together();
+} catch (error) {
+  print(error);
+} finally {
+  clean_up();
+}
+

+ +

+ Multiline Strings + Multiline strings are allowed in CoffeeScript. +

+
moby_dick: "Call me Ishmael. Some years ago --
+never mind how long precisely -- having little
+or no money in my purse, and nothing particular
+to interest me on shore, I thought I would sail
+about a little and see the watery part of the
+world..."
+
var moby_dick = "Call me Ishmael. Some years ago --\
+never mind how long precisely -- having little\
+or no money in my purse, and nothing particular\
+to interest me on shore, I thought I would sail\
+about a little and see the watery part of the\
+world...";
+

+ +
+
diff --git a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage index b788c281..29f52154 100644 --- a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage +++ b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage @@ -229,7 +229,7 @@ match - \b(super|this)\b + \b(super|this|extends)\b name variable.language.cs diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 39be9602..a1d759da 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -10,7 +10,7 @@ token TRY CATCH FINALLY THROW token BREAK CONTINUE token FOR IN WHILE token SWITCH CASE -token SUPER +token EXTENDS SUPER token DELETE token NEWLINE token JS @@ -28,8 +28,8 @@ prechigh right '-=' '+=' '/=' '*=' '||=' '&&=' right DELETE left "." - right THROW FOR IN WHILE - left UNLESS IF ELSE + right THROW FOR IN WHILE NEW + left UNLESS IF ELSE EXTENDS left ":" right RETURN preclow @@ -68,6 +68,7 @@ rule | Call | Code | Operation + | Extend ; # We have to take extra care to convert these statements into expressions. @@ -246,6 +247,11 @@ rule SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) } ; + # Extending a class. + Extend: + IDENTIFIER EXTENDS Expression { result = ExtendNode.new(val[0], val[2]) } + ; + # 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 78208a1c..fc363801 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -14,7 +14,7 @@ module CoffeeScript "break", "continue", "for", "in", "while", "switch", "case", - "super", + "extends", "super", "delete"] # Token matching regexes. diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index f91e7589..ec69cc84 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -110,6 +110,10 @@ module CoffeeScript STATEMENTS.include?(@value.to_s) end + def line_ending + @value.to_s[-1..-1] == ';' ? '' : ';' + end + def compile(indent, scope, opts={}) code = @value.to_s write(code) @@ -171,6 +175,20 @@ module CoffeeScript end end + class ExtendNode < Node + + attr_reader :subclass, :superclass + + def initialize(subclass, superclass) + @subclass, @superclass = subclass, superclass + end + + def compile(indent, scope, opts={}) + "#{@subclass}.prototype = #{@superclass.compile(indent, scope, opts)}" + end + + end + # A value, indexed or dotted into, or vanilla. class ValueNode < Node attr_reader :literal, :properties, :last @@ -269,7 +287,7 @@ module CoffeeScript def compile(indent, scope, opts={}) name = @variable.respond_to?(:compile) ? @variable.compile(indent, scope) : @variable - last = @variable.respond_to?(:last) ? @variable.last : name + last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s opts = opts.merge({:assign => name, :last_assign => last}) return write("#{@variable}: #{@value.compile(indent, scope, opts)}") if @context == :object return write("#{name} = #{@value.compile(indent, scope, opts)}") if @variable.properties?