diff --git a/documentation/cs/conditionals.cs b/documentation/cs/conditionals.cs index 253d912c..5d1f985e 100644 --- a/documentation/cs/conditionals.cs +++ b/documentation/cs/conditionals.cs @@ -6,4 +6,4 @@ if happy and knows_it date: if friday then sue else jill. -expensive ||= do_the_math() \ No newline at end of file +expensive ||: do_the_math() \ No newline at end of file diff --git a/documentation/cs/overview.cs b/documentation/cs/overview.cs new file mode 100644 index 00000000..8e44f8aa --- /dev/null +++ b/documentation/cs/overview.cs @@ -0,0 +1,22 @@ +# Assignment: +number: 42 +opposite_day: true + +# Conditions: +number: -42 if opposite_day + +# Functions: +square: x => x * x. + +# Arrays: +list: [1, 2, 3, 4, 5] + +# Objects: +math: { + root: Math.sqrt + square: square + cube: x => x * square(x). +} + +# Array comprehensions: +cubed_list: math.cube(num) for num in list. diff --git a/documentation/cs/try.cs b/documentation/cs/try.cs index 6664bbd1..8a2c6275 100644 --- a/documentation/cs/try.cs +++ b/documentation/cs/try.cs @@ -2,6 +2,6 @@ try all_hell_breaks_loose() cats_and_dogs_living_together() catch error - print( error ) + print(error) finally clean_up(). \ No newline at end of file diff --git a/documentation/css/docs.css b/documentation/css/docs.css index 3d11307b..dd2a4f86 100644 --- a/documentation/css/docs.css +++ b/documentation/css/docs.css @@ -30,32 +30,34 @@ b.header { margin: 40px 0 5px 0; font-size: 16px; } -table, tr, td { - margin: 0; padding: 0; +table { + margin: 16px 0 0 13px; padding: 0; + width: 625px; } - td { - padding: 2px 12px 2px 0; + tr, td { + margin: 0; padding: 0; } + td { + padding: 9px 15px 9px 0; + } code, pre, tt { font-family: Monaco, Consolas, "Lucida Console", monospace; font-size: 12px; line-height: 18px; - color: #555529; + color: #191955; } - code { - margin-left: 20px; + tt { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 85%; + padding: 0px 0.2em; } pre { + border-left: 6px solid #222255; + margin-left: 13px; + padding: 3px 0 3px 12px; font-size: 12px; - 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; @@ -67,4 +69,14 @@ div.code { div.code button { position: absolute; right: 8px; bottom: 8px; - } \ No newline at end of file + } + div.code pre { + float: left; + width: 410px; + border-left: 1px dotted #559; + padding: 0 0 0 12px; + margin: 0; + } + div.code pre:first-child { + border-left: 0; + } \ No newline at end of file diff --git a/documentation/index.html.erb b/documentation/index.html.erb index 8d2086d2..b2ea5486 100644 --- a/documentation/index.html.erb +++ b/documentation/index.html.erb @@ -2,6 +2,7 @@ require 'uv' def code_for(file, executable=false) @stripper ||= /(\A\(function\(\)\{\n|\}\)\(\);\Z|^ )/ + return '' unless File.exists?("documentation/js/#{file}.js") 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) @@ -30,10 +31,10 @@

CoffeeScript is a little language that compiles into JavaScript. Think - of it as JavaScript's simpleminded kid brother — the same genes, + of it as JavaScript's less ostentatious kid brother — the same genes, 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. + bonus goodies, statements in CoffeeScript correspond one-to-one with their + equivalent in JavaScript, it's just another way of saying it.

@@ -41,181 +42,303 @@

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. + explicit or implied, of its suitability for any purpose. That said, + it compiles into clean JavaScript (the good parts) that can use existing + JavaScript libraries seamlessly, and can pass through + JSLint without warnings. The compiled + scripts are quite readable — pretty-printed, with comments + preserved intact.

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. - -

- -

+ Mini Overview
+ Installation and Usage
Punctuation Primer
Functions and Invocation
- Objects and Arrays
Assignment
+ Objects and Arrays
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 + Calling Super from a Subclass
Embedded JavaScript
+ Aliases
Switch/Case/Else
Try/Catch/Finally
Multiline Strings

+

Mini Overview

+ +

CoffeeScript on the left, compiled JavaScript output on the right.

+ + <%= code_for('overview', 'cubed_list') %> + +

Installation and Usage

+ +
+sudo gem install coffee-script
+

- In all of the following examples, the source CoffeeScript is provided on - the left, and the direct compilation into JavaScript is on the right. + Installing the gem provides the coffee-script command, which can + be used to compile CoffeeScript .cs files into JavaScript, as + well as debug. By default, coffee-script writes out the + JavaScript as .js files in the same directory, but output + can be customized with the following options:

-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-o, --output [DIR] + Write out all compiled JavaScript files into the specified directory. +
-w, --watch + Watch the modification times of the named scripts, recompiling as + soon as a change occurs. +
-p, --print + Instead of writing out the JavaScript as a file, print it + directly to stdout. +
-l, --lint + If the jsl (JavaScript Lint) command is installed, use it + to check the compilation of a CoffeeScript file. +
-e, --eval + Compile and print a little snippet of CoffeeScript directly from the + command line. For example:
coffee-script -e "square: x => x * x." +
-t, --tokens + Instead of parsing the CoffeeScript, just lex it, and print out the + token stream: [:IDENTIFIER, "square"], [":", ":"], [:PARAM, "x"] ... +
-v, --verbose + As the JavaScript is being generated, print out every step of code + generation, including lexical scope and the node in the + AST. +
-

- 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. -

- +

+ Examples: +

-

- 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)') %> +
+coffee-script path/to/script.cs
+coffee-script --watch --lint experimental.cs
+coffee-script --print app/scripts/*.cs > concatenation.js
-

- 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(",")') %> -

-

+

Language Reference

-

- 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') %> -

-

+

+ + 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. + In all of the following examples, the source CoffeeScript is provided on + the left, and the direct compilation into JavaScript is on the right. + Familiarity with JavaScript is assumed, although it would certainly + be nice to have a tutorial that builds from the ground up in the future. + +

-

- 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. -

+

+ Punctuation Primer + You don't need to use semicolons ; to terminate expressions, ending + the line will do just as well. All other whitespace is + not significant. Instead of using curly braces { } + to delimit a block of code, use a period . to mark the end of a + function, if-statement, switch, or try/catch. +

-

- Conditionals, Ternaries, and Conditional Assignment -

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

-

+

+ Functions and Invocation + Let's start with the best part, shall we? Functions are defined + by a list of parameters, an arrow, and the function body. The empty + function looks like this: =>. +

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

- 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. -

+

+ Assignment + Use a colon : to assign, as in + JSON. Equal signs are only needed for + mathy things. +

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

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

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

+ Objects and Arrays + Object and Array literals look very similar to their JavaScript cousins. + When you spread out each assignment on a separate line, the commas are + optional. In this way, assigning object properties looks the same as + assigning local variables. +

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

- 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') %> +

+ 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. +

+

+ Although suppressed within this documentation, all + CoffeeScript output is wrapped in an anonymous function: + (function(){ ... })(); This safety wrapper, combined with the + automatic generation of the var keyword, make it exceedingly difficult + to pollute the global namespace by accident. +

-

- 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') %> +

+ Conditionals, Ternaries, and Conditional Assignment + If/else statements can be written without the use of parenthesis and + curly brackets. As with functions and other block expressions, conditionals + are closed with periods. No period is necessary when using the single-line + postfix form, with the if at the end. +

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

+ CoffeeScript includes the conditional assignment operators: ||:, + which only assigns a value to a variable if the variable's current value + is falsy, and &&:, which will only replace the value of + truthy variables. +

-

- 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) %> +

+ Everything is an Expression (at least, as much as possible) + 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 all statements in the + language can be used as expressions. Watch how the return gets + pushed down into each possible branch of execution, in the function + below. +

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

+ The same mechanism is used to push down assignment statements, switch + statements, and if-elses (although the ternary operator is preferred). +

-

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

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

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

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

+ Other JavaScript loops, such as for loops and do-while loops + can be mimicked by variations on while, but the hope is that you + won't need to do that with CoffeeScript, either because you're using + each (forEach) style iterators, or... +

-

- 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') %> +

+ Array Comprehensions + For your looping needs, CoffeeScript provides array comprehensions + similar to Python's. They replace (and compile into) for loops, with + optional guard clauses and the value of the current array index. + Unlike for loops, array comprehensions are expressions, and can be returned + and assigned. They should be able to handle most places where you otherwise + would use a loop, each/forEach, map, or select/filter. +

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

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

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

+ Array Slice Literals + CoffeeScript includes 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') %> -

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

- <%= code_for('strings') %> +

+ 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 that provide a cleaner + syntax for classical inheritance on top of JavaScript's prototypes: + Base2, + Prototype.js, + JS.Class, etc. +

+ <%= 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) %> + +

+ Aliases + Because the == operator frequently causes undesirable coercion, + is intransitive, and has a different meaning than in other languages, + CoffeeScript compiles == into ===, and != into + !==. +

+

+ is also compiles into ===, + and aint into !==. +

+

+ +

+ + +

+ 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/expressions.js b/documentation/js/expressions.js index 762f9720..9301da9c 100644 --- a/documentation/js/expressions.js +++ b/documentation/js/expressions.js @@ -3,11 +3,7 @@ if (student.excellent_work) { return "A+"; } else if (student.okay_stuff) { - return if (student.tried_hard) { - return "B"; - } else { - return "B-"; - }; + return student.tried_hard ? "B" : "B-"; } else { return "C"; } diff --git a/documentation/js/overview.js b/documentation/js/overview.js new file mode 100644 index 00000000..ee46bfd1 --- /dev/null +++ b/documentation/js/overview.js @@ -0,0 +1,33 @@ +(function(){ + + // Assignment: + var number = 42; + var opposite_day = true; + // Conditions: + if (opposite_day) { + number = -42; + } + // Functions: + var square = function(x) { + return x * x; + }; + // Arrays: + var list = [1, 2, 3, 4, 5]; + // Objects: + var math = { + root: Math.sqrt, + square: square, + cube: function(x) { + return x * square(x); + } + }; + // Array comprehensions: + var cubed_list; + var a = list; + var d = []; + for (var b=0, c=a.length; b race(). # Conditional assignment: -good ||= evil -wine &&= cheese +good ||: evil +wine &&: cheese # Nested property access and calls. ((moon.turn(360))).shapes[3].move({x: 45, y: 30}).position['top'].offset('x') diff --git a/index.html b/index.html index 99016737..4417434c 100644 --- a/index.html +++ b/index.html @@ -17,10 +17,10 @@

CoffeeScript is a little language that compiles into JavaScript. Think - of it as JavaScript's simpleminded kid brother — the same genes, + of it as JavaScript's less ostentatious kid brother — the same genes, 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. + bonus goodies, statements in CoffeeScript correspond one-to-one with their + equivalent in JavaScript, it's just another way of saying it.

@@ -28,61 +28,230 @@

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. + explicit or implied, of its suitability for any purpose. That said, + it compiles into clean JavaScript (the good parts) that can use existing + JavaScript libraries seamlessly, and can pass through + JSLint without warnings. The compiled + scripts are quite readable — pretty-printed, with comments + preserved intact.

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. - -

- -

+ Mini Overview
+ Installation and Usage
Punctuation Primer
Functions and Invocation
- Objects and Arrays
Assignment
+ Objects and Arrays
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 + Calling Super from a Subclass
Embedded JavaScript
+ Aliases
Switch/Case/Else
Try/Catch/Finally
Multiline Strings

+

Mini Overview

+ +

CoffeeScript on the left, compiled JavaScript output on the right.

+ +
# Assignment:
+number: 42
+opposite_day: true
+
+# Conditions:
+number: -42 if opposite_day
+
+# Functions:
+square: x => x * x.
+
+# Arrays:
+list: [1, 2, 3, 4, 5]
+
+# Objects:
+math: {
+  root:   Math.sqrt
+  square: square
+  cube:   x => x * square(x).
+}
+
+# Array comprehensions:
+cubed_list: math.cube(num) for num in list.
+
+// Assignment:
+var number = 42;
+var opposite_day = true;
+// Conditions:
+if (opposite_day) {
+  number = -42;
+}
+// Functions:
+var square = function(x) {
+  return x * x;
+};
+// Arrays:
+var list = [1, 2, 3, 4, 5];
+// Objects:
+var math = {
+  root: Math.sqrt,
+  square: square,
+  cube: function(x) {
+    return x * square(x);
+  }
+};
+// Array comprehensions:
+var cubed_list;
+var a = list;
+var d = [];
+for (var b=0, c=a.length; b<c; b++) {
+  var num = a[b];
+  d[b] = math.cube(num);
+}
+cubed_list = d;
+

+ +

Installation and Usage

+ +
+sudo gem install coffee-script
+

- In all of the following examples, the source CoffeeScript is provided on - the left, and the direct compilation into JavaScript is on the right. + Installing the gem provides the coffee-script command, which can + be used to compile CoffeeScript .cs files into JavaScript, as + well as debug. By default, coffee-script writes out the + JavaScript as .js files in the same directory, but output + can be customized with the following options:

-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-o, --output [DIR] + Write out all compiled JavaScript files into the specified directory. +
-w, --watch + Watch the modification times of the named scripts, recompiling as + soon as a change occurs. +
-p, --print + Instead of writing out the JavaScript as a file, print it + directly to stdout. +
-l, --lint + If the jsl (JavaScript Lint) command is installed, use it + to check the compilation of a CoffeeScript file. +
-e, --eval + Compile and print a little snippet of CoffeeScript directly from the + command line. For example:
coffee-script -e "square: x => x * x." +
-t, --tokens + Instead of parsing the CoffeeScript, just lex it, and print out the + token stream: [:IDENTIFIER, "square"], [":", ":"], [:PARAM, "x"] ... +
-v, --verbose + As the JavaScript is being generated, print out every step of code + generation, including lexical scope and the node in the + AST. +
-

- 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. -

- +

+ Examples: +

-

- 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.
+    
+coffee-script path/to/script.cs
+coffee-script --watch --lint experimental.cs
+coffee-script --print app/scripts/*.cs > concatenation.js
+ +

Language Reference

+ +

+ + 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. + In all of the following examples, the source CoffeeScript is provided on + the left, and the direct compilation into JavaScript is on the right. + Familiarity with JavaScript is assumed, although it would certainly + be nice to have a tutorial that builds from the ground up in the future. + +

+ +

+ Punctuation Primer + You don't need to use semicolons ; to terminate expressions, ending + the line will do just as well. All other whitespace is + not significant. Instead of using curly braces { } + to delimit a block of code, use a period . to mark the end of a + function, if-statement, switch, or try/catch. +

+ +

+ Functions and Invocation + Let's start with the best part, shall we? Functions are defined + by a list of parameters, an arrow, and the function body. The empty + function looks like this: =>. +

+
square: x => x * x.
 cube:   x => square(x) * x.
 
var square = function(x) {
   return x * x;
@@ -98,12 +267,28 @@ var cube = function(x) {
 };
 ;alert(cube(5));'>run: 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. -

-
song: ["do", "re", "mi", "fa", "so"]
+    

+ Assignment + Use a colon : to assign, as in + JSON. Equal signs are only needed for + mathy things. +

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

+ +

+ Objects and Arrays + Object and Array literals look very similar to their JavaScript cousins. + When you spread out each assignment on a separate line, the commas are + optional. In this way, assigning object properties looks the same as + assigning local variables. +

+
song: ["do", "re", "mi", "fa", "so"]
 ages: {
   max: 10
   ida: 9
@@ -122,31 +307,14 @@ var ages = {
   tim: 11
 };
 ;alert(song.join(","));'>run: 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. -

-
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
+    

+ 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.
@@ -166,17 +334,28 @@ var change_numbers = function() {
 };
 var new_num = change_numbers();
 ;alert(new_num);'>run: 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. -

+

+ 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. +

+

+ Although suppressed within this documentation, all + CoffeeScript output is wrapped in an anonymous function: + (function(){ ... })(); This safety wrapper, combined with the + automatic generation of the var keyword, make it exceedingly difficult + to pollute the global namespace by accident. +

-

- Conditionals, Ternaries, and Conditional Assignment -

-
mood: greatly_improved if singing
+    

+ Conditionals, Ternaries, and Conditional Assignment + If/else statements can be written without the use of parenthesis and + curly brackets. As with functions and other block expressions, conditionals + are closed with periods. No period is necessary when using the single-line + postfix form, with the if at the end. +

+
mood: greatly_improved if singing
 
 if happy and knows_it
   claps_hands()
@@ -196,17 +375,23 @@ expensive ||= do_the_m
 var date = friday ? sue : jill;
 expensive = expensive || do_the_math();
 

-

-

+

+ CoffeeScript includes the conditional assignment operators: ||:, + which only assigns a value to a variable if the variable's current value + is falsy, and &&:, which will only replace the value of + truthy variables. +

-

- 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 =>
+    

+ Everything is an Expression (at least, as much as possible) + 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 all statements in the + language can be used as expressions. Watch how the return gets + pushed down into each possible branch of execution, in the function + below. +

+
grade: student =>
   if student.excellent_work
     "A+"
   else if student.okay_stuff
@@ -219,11 +404,7 @@ eldest: if if (student.excellent_work) {
     return "A+";
   } else if (student.okay_stuff) {
-    return if (student.tried_hard) {
-      return "B";
-    } else {
-      return "B-";
-    };
+    return student.tried_hard ? "B" : "B-";
   } else {
     return "C";
   }
@@ -233,30 +414,23 @@ eldest: if  21 ? "Liz" : "Ike";
 ;alert(eldest);'>run: 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. -

+

+ The same mechanism is used to push down assignment statements, switch + statements, and if-elses (although the ternary operator is preferred). +

-

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

-
while demand > supply
+    

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

+
while demand > supply
   sell()
   restock().
 
@@ -269,16 +443,23 @@ var eldest = 24 > 21 ? "Liz" : "Ike";
   buy();
 }
 

+

+ Other JavaScript loops, such as for loops and do-while loops + can be mimicked by variations on while, but the hope is that you + won't need to do that with CoffeeScript, either because you're using + each (forEach) style iterators, or... +

-

- 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.
+    

+ Array Comprehensions + For your looping needs, CoffeeScript provides array comprehensions + similar to Python's. They replace (and compile into) for loops, with + optional guard clauses and the value of the current array index. + Unlike for loops, array comprehensions are expressions, and can be returned + and assigned. They should be able to handle most places where you otherwise + would use a loop, each/forEach, map, or select/filter. +

+
# Eat lunch.
 lunch: food.eat() for food in ['toast', 'cheese', 'wine'].
 
 # Zebra-stripe a table.
@@ -302,13 +483,13 @@ lunch = d;
 }
 

-

- 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]
+    

+ Array Slice Literals + CoffeeScript includes 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);
@@ -316,13 +497,16 @@ three_to_six: nums[3, <
 var three_to_six = nums.slice(3, 6 + 1);
 ;alert(three_to_six);'>run: 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 - ). -

-
Animal: => .
+    

+ 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 that provide a cleaner + syntax for classical inheritance on top of JavaScript's prototypes: + Base2, + Prototype.js, + JS.Class, etc. +

+
Animal: => .
 Animal.prototype.move: meters =>
   alert(this.name + " moved " + meters + "m.").
 
@@ -395,12 +579,12 @@ sam.move();
 tom.move();
 ;'>run
-

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

-
js: => `alert("Hello JavaScript");`.
+    

+ 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() {
@@ -417,16 +601,32 @@ if (10 > 9) {
 }
 ;'>run
-

- 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
+    

+ Aliases + Because the == operator frequently causes undesirable coercion, + is intransitive, and has a different meaning than in other languages, + CoffeeScript compiles == into ===, and != into + !==. +

+

+ is also compiles into ===, + and aint into !==. +

+

+ +

+ + +

+ 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"
@@ -438,9 +638,7 @@ if (10 > 9) {
 } else if (day === "Wednesday") {
   go_to_the_park();
 } else if (day === "Saturday") {
-  if (day === bingo_day) {
-    go_to_bingo();
-  };
+  day === bingo_day ? go_to_bingo() : null;
 } else if (day === "Sunday") {
   go_to_church();
 } else {
@@ -448,16 +646,16 @@ if (10 > 9) {
 }
 

-

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

-
try
+    

+ 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 )
+  print(error)
 finally
   clean_up().
 
try {
@@ -470,11 +668,11 @@ if (10 > 9) {
 }
 

-

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

-
moby_dick: "Call me Ishmael. Some years ago --
+    

+ 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
@@ -488,8 +686,6 @@ if (10 > 9) {
 world...";
 

-
-
diff --git a/lib/coffee_script/command_line.rb b/lib/coffee_script/command_line.rb index 2c313bdf..1172d7c2 100644 --- a/lib/coffee_script/command_line.rb +++ b/lib/coffee_script/command_line.rb @@ -41,12 +41,13 @@ Usage: # Compiles (or partially compiles) the source CoffeeScript file, returning # the desired JS, tokens, or lint results. def compile_javascript(source) - return tokens(source) if @options[:tokens] - contents = compile(source) - return unless contents - return puts(contents) if @options[:print] - return lint(contents) if @options[:lint] - File.open(path_for(source), 'w+') {|f| f.write(contents) } + script = File.read(source) + return tokens(script) if @options[:tokens] + js = compile(script, source) + return unless js + return puts(js) if @options[:print] + return lint(js) if @options[:lint] + File.open(path_for(source), 'w+') {|f| f.write(js) } end # Spins up a watcher thread to keep track of the modification times of the @@ -92,18 +93,22 @@ Usage: # Eval a little piece of CoffeeScript directly from the command line. def eval_scriptlet - puts CoffeeScript.compile(@sources.join(' ')) + script = @sources.join(' ') + return tokens(script) if @options[:tokens] + js = compile(script) + return lint(js) if @options[:lint] + puts js end # Print the tokens that the lexer generates from a source script. - def tokens(source) - puts Lexer.new.tokenize(File.read(source)).inspect + def tokens(script) + puts Lexer.new.tokenize(script).inspect end # Compile a single source file to JavaScript. - def compile(source) + def compile(script, source='') begin - CoffeeScript.compile(File.open(source)) + CoffeeScript.compile(script) rescue CoffeeScript::ParseError => e STDERR.puts e.message(source) exit(1) unless @options[:watch] diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 47bb9835..ef00e4c8 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -26,12 +26,12 @@ prechigh left '<=' '<' '>' '>=' right '==' '!=' IS AINT left '&&' '||' AND OR - right '-=' '+=' '/=' '*=' '||=' '&&=' + right '-=' '+=' '/=' '*=' right DELETE left "." right THROW FOR IN WHILE NEW left UNLESS IF ELSE - left ":" + left ":" '||:' '&&:' right RETURN preclow @@ -173,8 +173,8 @@ rule | Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) } | Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) } | Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) } - | Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) } - | Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) } + | Expression '||:' Expression { result = OpNode.new(val[1], val[0], val[2]) } + | Expression '&&:' Expression { result = OpNode.new(val[1], val[0], val[2]) } | DELETE Expression { result = OpNode.new(val[0], val[1]) } ; diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 9016ab39..5cf50aa0 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -112,6 +112,7 @@ module CoffeeScript # Matches and consumes comments. def comment_token return false unless comment = @chunk[COMMENT, 1] + @line += comment.scan(MULTILINER).length token(:COMMENT, comment.gsub(COMMENT_CLEANER, '').split(MULTILINER)) token("\n", "\n") @i += comment.length diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 1b5d2a89..7328e402 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -342,7 +342,7 @@ module CoffeeScript "aint" => "!==", 'not' => '!', } - CONDITIONALS = ['||=', '&&='] + CONDITIONALS = ['||:', '&&:'] attr_reader :operator, :first, :second