Issue #901 ... allow constructor functions to maintain their position in the class body.

This commit is contained in:
Jeremy Ashkenas
2010-12-06 01:01:57 -05:00
parent 2decb30d4e
commit ec64646fee
4 changed files with 60 additions and 47 deletions

View File

@@ -162,10 +162,10 @@
return Base;
}();
exports.Expressions = Expressions = function() {
__extends(Expressions, Base);
function Expressions(nodes) {
this.expressions = compact(flatten(nodes || []));
}
__extends(Expressions, Base);
Expressions.prototype.children = ['expressions'];
Expressions.prototype.push = function(node) {
this.expressions.push(node);
@@ -298,10 +298,10 @@
return Expressions;
}();
exports.Literal = Literal = function() {
__extends(Literal, Base);
function Literal(value) {
this.value = value;
}
__extends(Literal, Base);
Literal.prototype.makeReturn = function() {
if (this.isPureStatement()) {
return this;
@@ -333,10 +333,10 @@
return Literal;
}();
exports.Return = Return = function() {
__extends(Return, Base);
function Return(expression) {
this.expression = expression;
}
__extends(Return, Base);
Return.prototype.children = ['expression'];
Return.prototype.isStatement = YES;
Return.prototype.isPureStatement = YES;
@@ -357,6 +357,7 @@
return Return;
}();
exports.Value = Value = function() {
__extends(Value, Base);
function Value(base, props, tag) {
if (!props && base instanceof Value) {
return base;
@@ -368,7 +369,6 @@
}
return this;
}
__extends(Value, Base);
Value.prototype.children = ['base', 'properties'];
Value.prototype.push = function(prop) {
this.properties.push(prop);
@@ -492,10 +492,10 @@
return Value;
}();
exports.Comment = Comment = function() {
__extends(Comment, Base);
function Comment(comment) {
this.comment = comment;
}
__extends(Comment, Base);
Comment.prototype.isPureStatement = YES;
Comment.prototype.isStatement = YES;
Comment.prototype.makeReturn = THIS;
@@ -510,6 +510,7 @@
return Comment;
}();
exports.Call = Call = function() {
__extends(Call, Base);
function Call(variable, args, soak) {
this.args = args != null ? args : [];
this.soak = soak;
@@ -517,7 +518,6 @@
this.isSuper = variable === 'super';
this.variable = this.isSuper ? null : variable;
}
__extends(Call, Base);
Call.prototype.children = ['variable', 'args'];
Call.prototype.newInstance = function() {
var base;
@@ -647,11 +647,11 @@
return Call;
}();
exports.Extends = Extends = function() {
__extends(Extends, Base);
function Extends(child, parent) {
this.child = child;
this.parent = parent;
}
__extends(Extends, Base);
Extends.prototype.children = ['child', 'parent'];
Extends.prototype.compile = function(o) {
utility('hasProp');
@@ -660,12 +660,12 @@
return Extends;
}();
exports.Access = Access = function() {
__extends(Access, Base);
function Access(name, tag) {
this.name = name;
this.proto = tag === 'proto' ? '.prototype' : '';
this.soak = tag === 'soak';
}
__extends(Access, Base);
Access.prototype.children = ['name'];
Access.prototype.compile = function(o) {
var name;
@@ -676,10 +676,10 @@
return Access;
}();
exports.Index = Index = function() {
__extends(Index, Base);
function Index(index) {
this.index = index;
}
__extends(Index, Base);
Index.prototype.children = ['index'];
Index.prototype.compile = function(o) {
return (this.proto ? '.prototype' : '') + ("[" + (this.index.compile(o, LEVEL_PAREN)) + "]");
@@ -690,14 +690,14 @@
return Index;
}();
exports.Range = Range = function() {
__extends(Range, Base);
Range.prototype.children = ['from', 'to'];
function Range(from, to, tag) {
this.from = from;
this.to = to;
this.exclusive = tag === 'exclusive';
this.equals = this.exclusive ? '' : '=';
}
__extends(Range, Base);
Range.prototype.children = ['from', 'to'];
Range.prototype.compileVariables = function(o) {
var parts, _ref, _ref2, _ref3;
o = merge(o, {
@@ -775,12 +775,12 @@
return Range;
}();
exports.Slice = Slice = function() {
__extends(Slice, Base);
Slice.prototype.children = ['range'];
function Slice(range) {
this.range = range;
Slice.__super__.constructor.call(this);
}
__extends(Slice, Base);
Slice.prototype.children = ['range'];
Slice.prototype.compileNode = function(o) {
var from, to;
from = this.range.from ? this.range.from.compile(o) : '0';
@@ -794,11 +794,11 @@
return Slice;
}();
exports.Obj = Obj = function() {
__extends(Obj, Base);
function Obj(props, generated) {
this.generated = generated != null ? generated : false;
this.objects = this.properties = props || [];
}
__extends(Obj, Base);
Obj.prototype.children = ['properties'];
Obj.prototype.compileNode = function(o) {
var i, idt, indent, join, lastNoncom, nonComments, obj, prop, props, _i, _len, _len2, _ref, _results, _results2;
@@ -860,10 +860,10 @@
return Obj;
}();
exports.Arr = Arr = function() {
__extends(Arr, Base);
function Arr(objs) {
this.objects = objs || [];
}
__extends(Arr, Base);
Arr.prototype.children = ['objects'];
Arr.prototype.compileNode = function(o) {
var code, obj, _i, _len, _ref, _results;
@@ -903,13 +903,13 @@
return Arr;
}();
exports.Class = Class = function() {
__extends(Class, Base);
function Class(variable, parent, body) {
this.variable = variable;
this.parent = parent;
this.body = body != null ? body : new Expressions;
this.boundFuncs = [];
}
__extends(Class, Base);
Class.prototype.children = ['variable', 'parent', 'body'];
Class.prototype.determineName = function() {
var decl, tail;
@@ -961,11 +961,10 @@
throw new Error('cannot define a constructor as a bound function');
}
if (func instanceof Code) {
this.ctor = func;
assign = this.ctor = func;
} else {
this.ctor = new Assign(new Value(new Literal(name)), func);
assign = this.ctor = new Assign(new Value(new Literal(name)), func);
}
assign = null;
} else {
if (!assign.variable["this"]) {
assign.variable = new Value(new Literal(name), [new Access(base, 'proto')]);
@@ -991,10 +990,10 @@
for (i = 0, _len = _ref.length; i < _len; i++) {
node = _ref[i];
if (node instanceof Value && node.isObject(true)) {
exps[i] = compact(this.addProperties(node, name));
exps[i] = this.addProperties(node, name);
}
}
return child.expressions = exps = compact(flatten(exps));
return child.expressions = exps = flatten(exps);
}
}, this));
};
@@ -1004,6 +1003,7 @@
if (this.parent) {
this.ctor.body.push(new Call('super', [new Splat(new Literal('arguments'))]));
}
this.body.expressions.unshift(this.ctor);
}
this.ctor.ctor = this.ctor.name = name;
this.ctor.klass = null;
@@ -1016,11 +1016,10 @@
lname = new Literal(name);
this.setContext(name);
this.walkBody(name);
this.ensureConstructor(name);
if (this.parent) {
this.body.expressions.unshift(new Extends(lname, this.parent));
}
this.body.expressions.unshift(this.ctor);
this.ensureConstructor(name);
this.body.expressions.push(lname);
this.addBoundFunctions(o);
klass = new Parens(new Call(new Code([], this.body)), true);
@@ -1032,12 +1031,12 @@
return Class;
}();
exports.Assign = Assign = function() {
__extends(Assign, Base);
function Assign(variable, value, context) {
this.variable = variable;
this.value = value;
this.context = context;
}
__extends(Assign, Base);
Assign.prototype.METHOD_DEF = /^(?:(\S+)\.prototype\.|\S+?)?\b([$A-Za-z_][$\w]*)$/;
Assign.prototype.children = ['variable', 'value'];
Assign.prototype.assigns = function(name) {
@@ -1190,6 +1189,7 @@
return Assign;
}();
exports.Code = Code = function() {
__extends(Code, Base);
function Code(params, body, tag) {
this.params = params || [];
this.body = body || new Expressions;
@@ -1198,7 +1198,6 @@
this.context = 'this';
}
}
__extends(Code, Base);
Code.prototype.children = ['params', 'body'];
Code.prototype.isStatement = function() {
return !!this.ctor;
@@ -1298,12 +1297,12 @@
return Code;
}();
exports.Param = Param = function() {
__extends(Param, Base);
function Param(name, value, splat) {
this.name = name;
this.value = value;
this.splat = splat;
}
__extends(Param, Base);
Param.prototype.children = ['name', 'value'];
Param.prototype.compile = function(o) {
return this.name.compile(o, LEVEL_LIST);
@@ -1334,12 +1333,12 @@
return Param;
}();
exports.Splat = Splat = function() {
function Splat(name) {
this.name = name.compile ? name : new Literal(name);
}
__extends(Splat, Base);
Splat.prototype.children = ['name'];
Splat.prototype.isAssignable = YES;
function Splat(name) {
this.name = name.compile ? name : new Literal(name);
}
Splat.prototype.assigns = function(name) {
return this.name.assigns(name);
};
@@ -1389,11 +1388,11 @@
return Splat;
}();
exports.While = While = function() {
__extends(While, Base);
function While(condition, options) {
this.condition = (options != null ? options.invert : void 0) ? condition.invert() : condition;
this.guard = options != null ? options.guard : void 0;
}
__extends(While, Base);
While.prototype.children = ['condition', 'guard', 'body'];
While.prototype.isStatement = YES;
While.prototype.makeReturn = function() {
@@ -1452,6 +1451,7 @@
}();
exports.Op = Op = function() {
var CONVERSIONS, INVERSIONS;
__extends(Op, Base);
function Op(op, first, second, flip) {
if (op === 'in') {
return new In(first, second);
@@ -1470,7 +1470,6 @@
this.flip = !!flip;
return this;
}
__extends(Op, Base);
CONVERSIONS = {
'==': '===',
'!=': '!==',
@@ -1583,11 +1582,11 @@
return Op;
}();
exports.In = In = function() {
__extends(In, Base);
function In(object, array) {
this.object = object;
this.array = array;
}
__extends(In, Base);
In.prototype.children = ['object', 'array'];
In.prototype.invert = NEGATE;
In.prototype.compileNode = function(o) {
@@ -1637,13 +1636,13 @@
return In;
}();
exports.Try = Try = function() {
__extends(Try, Base);
function Try(attempt, error, recovery, ensure) {
this.attempt = attempt;
this.error = error;
this.recovery = recovery;
this.ensure = ensure;
}
__extends(Try, Base);
Try.prototype.children = ['attempt', 'recovery', 'ensure'];
Try.prototype.isStatement = YES;
Try.prototype.makeReturn = function() {
@@ -1665,10 +1664,10 @@
return Try;
}();
exports.Throw = Throw = function() {
__extends(Throw, Base);
function Throw(expression) {
this.expression = expression;
}
__extends(Throw, Base);
Throw.prototype.children = ['expression'];
Throw.prototype.isStatement = YES;
Throw.prototype.makeReturn = THIS;
@@ -1678,10 +1677,10 @@
return Throw;
}();
exports.Existence = Existence = function() {
__extends(Existence, Base);
function Existence(expression) {
this.expression = expression;
}
__extends(Existence, Base);
Existence.prototype.children = ['expression'];
Existence.prototype.invert = NEGATE;
Existence.prototype.compileNode = function(o) {
@@ -1697,10 +1696,10 @@
return Existence;
}();
exports.Parens = Parens = function() {
__extends(Parens, Base);
function Parens(body) {
this.body = body;
}
__extends(Parens, Base);
Parens.prototype.children = ['body'];
Parens.prototype.unwrap = function() {
return this.body;
@@ -1729,6 +1728,7 @@
return Parens;
}();
exports.For = For = function() {
__extends(For, Base);
function For(body, source, name, index) {
var _ref;
this.name = name;
@@ -1753,7 +1753,6 @@
}
this.returns = false;
}
__extends(For, Base);
For.prototype.children = ['body', 'source', 'guard', 'step'];
For.prototype.isStatement = YES;
For.prototype.makeReturn = function() {
@@ -1877,12 +1876,12 @@
return For;
}();
exports.Switch = Switch = function() {
__extends(Switch, Base);
function Switch(subject, cases, otherwise) {
this.subject = subject;
this.cases = cases;
this.otherwise = otherwise;
}
__extends(Switch, Base);
Switch.prototype.children = ['subject', 'cases', 'otherwise'];
Switch.prototype.isStatement = YES;
Switch.prototype.makeReturn = function() {
@@ -1938,6 +1937,7 @@
return Switch;
}();
exports.If = If = function() {
__extends(If, Base);
function If(condition, body, options) {
this.body = body;
if (options == null) {
@@ -1948,7 +1948,6 @@
this.isChain = false;
this.soak = options.soak;
}
__extends(If, Base);
If.prototype.children = ['condition', 'body', 'elseBody'];
If.prototype.bodyNode = function() {
var _ref;

View File

@@ -2,6 +2,7 @@
var Scope, extend, last, _ref;
_ref = require('./helpers'), extend = _ref.extend, last = _ref.last;
exports.Scope = Scope = function() {
Scope.root = null;
function Scope(parent, expressions, method) {
this.parent = parent;
this.expressions = expressions;
@@ -17,7 +18,6 @@
Scope.root = this;
}
}
Scope.root = null;
Scope.prototype.add = function(name, type) {
var pos;
if (typeof (pos = this.positions[name]) === 'number') {

View File

@@ -775,10 +775,9 @@ exports.Class = class Class extends Base
if func.bound
throw new Error 'cannot define a constructor as a bound function'
if func instanceof Code
@ctor = func
assign = @ctor = func
else
@ctor = new Assign(new Value(new Literal name), func)
assign = null
assign = @ctor = new Assign(new Value(new Literal name), func)
else
unless assign.variable.this
assign.variable = new Value(new Literal(name), [new Access(base, 'proto')])
@@ -794,8 +793,8 @@ exports.Class = class Class extends Base
if child instanceof Expressions
for node, i in exps = child.expressions
if node instanceof Value and node.isObject(true)
exps[i] = compact @addProperties node, name
child.expressions = exps = compact flatten exps
exps[i] = @addProperties node, name
child.expressions = exps = flatten exps
# Make sure that a constructor is defined for the class, and properly
# configured.
@@ -803,6 +802,7 @@ exports.Class = class Class extends Base
if not @ctor
@ctor = new Code
@ctor.body.push new Call 'super', [new Splat new Literal 'arguments'] if @parent
@body.expressions.unshift @ctor
@ctor.ctor = @ctor.name = name
@ctor.klass = null
@ctor.noReturn = yes
@@ -817,9 +817,8 @@ exports.Class = class Class extends Base
@setContext name
@walkBody name
@ensureConstructor name
@body.expressions.unshift new Extends lname, @parent if @parent
@body.expressions.unshift @ctor
@ensureConstructor name
@body.expressions.push lname
@addBoundFunctions o

View File

@@ -331,3 +331,18 @@ class Outer
eq (new Outer).label, 'outer'
eq (new Outer.Inner).label, 'inner'
# Variables in constructor bodies are correctly scoped.
class A
x = 1
constructor: ->
x = 10
y = 20
y = 2
captured: ->
{x, y}
a = new A
eq a.captured().x, 10
eq a.captured().y, 2