diff --git a/documentation/cs/super.cs b/documentation/cs/super.cs index baee074a..07cb3b31 100644 --- a/documentation/cs/super.cs +++ b/documentation/cs/super.cs @@ -3,13 +3,13 @@ Animal.prototype.move: meters => alert(this.name + " moved " + meters + "m."). Snake: name => this.name: name. -Snake.prototype: new Animal() +Snake extends new Animal() Snake.prototype.move: => alert("Slithering...") super(5). Horse: name => this.name: name. -Horse.prototype: new Animal() +Horse extends new Animal() Horse.prototype.move: => alert("Galloping...") super(45). diff --git a/documentation/index.html.erb b/documentation/index.html.erb index 167f8c5c..b51687d2 100644 --- a/documentation/index.html.erb +++ b/documentation/index.html.erb @@ -67,7 +67,7 @@ While Loops
Array Comprehensions
Array Slice Literals
- Calling Super from a Subclass
+ Inheritance, and Calling Super from a Subclass
Embedded JavaScript
Switch/When/Else
Try/Catch/Finally
@@ -332,8 +332,8 @@ coffee-script --print app/scripts/*.cs > concatenation.js

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

- Calling Super from a Subclass +

+ 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 that provide a cleaner syntax for classical inheritance on top of JavaScript's prototypes: @@ -341,9 +341,11 @@ coffee-script --print app/scripts/*.cs > concatenation.js Prototype.js, JS.Class, etc. The libraries provide syntactic sugar, but the built-in inheritance would - be completely usable if it weren't for one small exception: - it's very awkward to call super, the prototype object's - implementation of the current function. CoffeeScript converts + be completely usable if it weren't for a couple of small exceptions: + it's awkward to call super (the prototype object's + implementation of the current function), and it's awkward to correctly + set the prototype chain. CoffeeScript provides extends + to help with prototype setup, and converts super() calls into calls against the immediate ancestor's method of the same name.

diff --git a/documentation/js/super.js b/documentation/js/super.js index d9199aff..64c3c4f9 100644 --- a/documentation/js/super.js +++ b/documentation/js/super.js @@ -8,19 +8,19 @@ this.name = name; return this.name; }; - Snake.prototype = new Animal(); + Snake.prototype.__proto__ = new Animal(); Snake.prototype.move = function() { alert("Slithering..."); - return this.constructor.prototype.move.call(this, 5); + return Snake.prototype.__proto__.move.call(this, 5); }; var Horse = function(name) { this.name = name; return this.name; }; - Horse.prototype = new Animal(); + Horse.prototype.__proto__ = new Animal(); Horse.prototype.move = function() { alert("Galloping..."); - return this.constructor.prototype.move.call(this, 45); + return Horse.prototype.__proto__.move.call(this, 45); }; var sam = new Snake("Sammy the Python"); var tom = new Horse("Tommy the Palomino"); diff --git a/examples/code.cs b/examples/code.cs index 919a991b..4b135cdf 100644 --- a/examples/code.cs +++ b/examples/code.cs @@ -145,13 +145,13 @@ Animal.prototype.move: meters => alert(this.name + " moved " + meters + "m."). Snake: name => this.name: name. -Snake.prototype: Animal +Snake extends new Animal() Snake.prototype.move: => alert('Slithering...') super(5). Horse: name => this.name: name. -Horse.prototype: Animal +Horse extends new Animal() Horse.prototype.move: => alert('Galloping...') super(45). diff --git a/index.html b/index.html index cc4910f1..ac8b3ecb 100644 --- a/index.html +++ b/index.html @@ -53,7 +53,7 @@ While Loops
Array Comprehensions
Array Slice Literals
- Calling Super from a Subclass
+ Inheritance, and Calling Super from a Subclass
Embedded JavaScript
Switch/When/Else
Try/Catch/Finally
@@ -565,8 +565,8 @@ three_to_six: nums[3, < var three_to_six = nums.slice(3, 6 + 1); ;alert(three_to_six);'>run: three_to_six
-

- Calling Super from a Subclass +

+ 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 that provide a cleaner syntax for classical inheritance on top of JavaScript's prototypes: @@ -574,9 +574,11 @@ var three_to_six = nums.slice(3, 6 + 1); Prototype.js, JS.Class, etc. The libraries provide syntactic sugar, but the built-in inheritance would - be completely usable if it weren't for one small exception: - it's very awkward to call super, the prototype object's - implementation of the current function. CoffeeScript converts + be completely usable if it weren't for a couple of small exceptions: + it's awkward to call super (the prototype object's + implementation of the current function), and it's awkward to correctly + set the prototype chain. CoffeeScript provides extends + to help with prototype setup, and converts super() calls into calls against the immediate ancestor's method of the same name.

@@ -585,13 +587,13 @@ var three_to_six = nums.slice(3, 6 + 1); alert(this.name + " moved " + meters + "m."). Snake: name => this.name: name. -Snake.prototype: new Animal() +Snake extends new Animal() Snake.prototype.move: => alert("Slithering...") super(5). Horse: name => this.name: name. -Horse.prototype: new Animal() +Horse extends new Animal() Horse.prototype.move: => alert("Galloping...") super(45). @@ -614,19 +616,19 @@ tom.move() this.name = name; return this.name; }; -Snake.prototype = new Animal(); +Snake.prototype.__proto__ = new Animal(); Snake.prototype.move = function() { alert("Slithering..."); - return this.constructor.prototype.move.call(this, 5); + return Snake.prototype.__proto__.move.call(this, 5); }; var Horse = function(name) { this.name = name; return this.name; }; -Horse.prototype = new Animal(); +Horse.prototype.__proto__ = new Animal(); Horse.prototype.move = function() { alert("Galloping..."); - return this.constructor.prototype.move.call(this, 45); + return Horse.prototype.__proto__.move.call(this, 45); }; var sam = new Snake("Sammy the Python"); var tom = new Horse("Tommy the Palomino"); @@ -641,19 +643,19 @@ var Snake = function(name) { this.name = name; return this.name; }; -Snake.prototype = new Animal(); +Snake.prototype.__proto__ = new Animal(); Snake.prototype.move = function() { alert("Slithering..."); - return this.constructor.prototype.move.call(this, 5); + return Snake.prototype.__proto__.move.call(this, 5); }; var Horse = function(name) { this.name = name; return this.name; }; -Horse.prototype = new Animal(); +Horse.prototype.__proto__ = new Animal(); Horse.prototype.move = function() { alert("Galloping..."); - return this.constructor.prototype.move.call(this, 45); + return Horse.prototype.__proto__.move.call(this, 45); }; var sam = new Snake("Sammy the Python"); var tom = new Horse("Tommy the Palomino"); diff --git a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage index af4540fd..0ef633a1 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 c31d5196..857847e5 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -11,7 +11,7 @@ token BREAK CONTINUE token FOR IN WHILE token SWITCH WHEN token DELETE INSTANCEOF TYPEOF -token SUPER +token SUPER EXTENDS token NEWLINE token COMMENT token JS @@ -29,8 +29,8 @@ prechigh right '-=' '+=' '/=' '*=' right DELETE INSTANCEOF TYPEOF left "." - right THROW FOR IN WHILE NEW - left UNLESS IF ELSE + right THROW FOR IN WHILE NEW SUPER + left UNLESS IF ELSE EXTENDS left ":" '||:' '&&:' right RETURN preclow @@ -81,6 +81,7 @@ rule | While | For | Switch + | Extends | Comment ; @@ -254,6 +255,11 @@ rule | Super { result = val[0] } ; + # Extending an object's prototype. + Extends: + Value EXTENDS Expression { result = ExtendsNode.new(val[0], val[2]) } + ; + # A generic function invocation. Invocation: Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) } diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 683de472..50d66976 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", "when", - "super", + "super", "extends", "delete", "instanceof", "typeof"] # Token matching regexes. diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index bced2670..076559ff 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -215,6 +215,20 @@ module CoffeeScript end end + # Node to extend an object's prototype with an ancestor object. + class ExtendsNode < Node + attr_reader :sub_object, :super_object + + def initialize(sub_object, super_object) + @sub_object, @super_object = sub_object, super_object + end + + def compile(o={}) + "#{@sub_object.compile(o)}.prototype.__proto__ = #{@super_object.compile(o)}" + end + + end + # A value, indexed or dotted into, or vanilla. class ValueNode < Node attr_reader :literal, :properties, :last diff --git a/test/fixtures/execution/calling_super.cs b/test/fixtures/execution/calling_super.cs index e729db1e..69192449 100644 --- a/test/fixtures/execution/calling_super.cs +++ b/test/fixtures/execution/calling_super.cs @@ -3,17 +3,17 @@ Base.prototype.func: string => 'zero/' + string. FirstChild: => . -FirstChild.prototype.__proto__: new Base() +FirstChild extends new Base() FirstChild.prototype.func: string => super('one/') + string. SecondChild: => . -SecondChild.prototype.__proto__: new FirstChild() +SecondChild extends new FirstChild() SecondChild.prototype.func: string => super('two/') + string. ThirdChild: => . -ThirdChild.prototype.__proto__: new SecondChild() +ThirdChild extends new SecondChild() ThirdChild.prototype.func: string => super('three/') + string.