nodes: refactored and removed the notion of `o.top` in favor of LVL_TOP

This commit is contained in:
satyr 2010-10-23 18:06:04 +09:00
parent b82f495ec7
commit 2bc2c4717e
7 changed files with 365 additions and 431 deletions

View File

@ -6,7 +6,9 @@
return eval(CoffeeScript.compile(code, options));
};
CoffeeScript.run = function(code, options) {
options != null ? options.bare = true : undefined;
if (options != null) {
options.bare = true;
}
return Function(CoffeeScript.compile(code, options))();
};
if (!(typeof window !== "undefined" && window !== null)) {

View File

@ -4,6 +4,6 @@
for (key in _ref = require('./coffee-script')) {
if (!__hasProp.call(_ref, key)) continue;
val = _ref[key];
(exports[key] = val);
exports[key] = val;
}
}).call(this);

View File

@ -382,7 +382,6 @@
break;
case '::':
prev[0] = 'INDEX_PROTO';
break;
}
}
}

View File

@ -1,5 +1,5 @@
(function() {
var Accessor, ArrayLiteral, Assign, Base, Call, Class, Closure, Code, Comment, Existence, Expressions, Extends, For, IDENTIFIER, IS_STRING, If, In, Index, LVL_ACCESS, LVL_ASSIGN, LVL_COND, LVL_LIST, LVL_OP, LVL_PAREN, LVL_TOP, Literal, NO, NUMBER, ObjectLiteral, Op, Param, Parens, Push, Return, SIMPLENUM, Scope, Splat, Switch, TAB, THIS, TRAILING_WHITESPACE, Throw, Try, UTILITIES, Value, While, YES, _ref, compact, del, ends, extend, flatten, last, merge, starts, utility;
var Accessor, ArrayLiteral, Assign, Base, Call, Class, Closure, Code, Comment, Existence, Expressions, Extends, For, IDENTIFIER, IS_STRING, If, In, Index, LVL_ACCESS, LVL_COND, LVL_LIST, LVL_OP, LVL_PAREN, LVL_TOP, Literal, NO, NUMBER, ObjectLiteral, Op, Param, Parens, Push, Return, SIMPLENUM, Scope, Splat, Switch, TAB, THIS, TRAILING_WHITESPACE, Throw, Try, UTILITIES, Value, While, YES, _ref, compact, del, ends, extend, flatten, last, merge, multident, starts, utility;
var __extends = function(child, parent) {
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
@ -31,14 +31,14 @@
return Base;
})();
Base.prototype.compile = function(o, lvl) {
var top;
var node;
o = o ? extend({}, o) : {};
if (lvl) {
if (lvl != null) {
o.level = lvl;
}
top = this.topSensitive ? o.top : del(o, 'top');
this.tab = o.indent;
return top || o.asStatement || this instanceof Comment || this.isPureStatement() || !this.isStatement(o) ? this.compileNode(o) : this.compileClosure(o);
node = this.unfoldSoak(o) || this;
node.tab = o.indent;
return o.level === LVL_TOP || node.isPureStatement() || !node.isStatement(o) ? node.compileNode(o) : node.compileClosure(o);
};
Base.prototype.compileClosure = function(o) {
if (this.containsPureStatement()) {
@ -60,7 +60,7 @@
};
Base.prototype.compileLoopReference = function(o, name) {
var src, tmp;
src = tmp = this.compile(o, LVL_ASSIGN);
src = tmp = this.compile(o, LVL_LIST);
if (!(NUMBER.test(src) || IDENTIFIER.test(src) && o.scope.check(src, {
immediate: true
}))) {
@ -98,7 +98,7 @@
Base.prototype.toString = function(idt, override) {
var _i, _len, _ref2, _result, child, children, klass;
idt || (idt = '');
children = (function() {
children = ((function() {
_ref2 = this.collectChildren();
_result = [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
@ -106,7 +106,7 @@
_result.push(child.toString(idt + TAB));
}
return _result;
}).call(this).join('');
}).call(this)).join('');
klass = override || this.constructor.name + (this.soakNode ? '?' : '');
return '\n' + idt + klass + children;
};
@ -155,7 +155,6 @@
Base.prototype.isPureStatement = NO;
Base.prototype.isComplex = YES;
Base.prototype.isChainable = NO;
Base.prototype.topSensitive = false;
Base.prototype.unfoldSoak = NO;
Base.prototype.assigns = NO;
return Base;
@ -204,15 +203,16 @@
};
Expressions.prototype.compileNode = function(o) {
var _i, _len, _ref2, _result, node;
return (function() {
this.tab = o.indent;
return ((function() {
_ref2 = this.expressions;
_result = [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
node = _ref2[_i];
_result.push(this.compileExpression(node, merge(o)));
_result.push(this.compileExpression(node, o));
}
return _result;
}).call(this).join('\n');
}).call(this)).join('\n');
};
Expressions.prototype.compileRoot = function(o) {
var code;
@ -224,20 +224,25 @@
return o.bare ? code : "(function() {\n" + code + "\n}).call(this);\n";
};
Expressions.prototype.compileWithDeclarations = function(o) {
var code;
var code, scope;
code = this.compileNode(o);
if (o.scope.hasAssignments(this)) {
code = "" + this.tab + "var " + (o.scope.compiledAssignments().replace(/\n/g, '$&' + this.tab)) + ";\n" + code;
scope = o.scope;
if (scope.hasAssignments(this)) {
code = "" + this.tab + "var " + (multident(scope.compiledAssignments(), this.tab)) + ";\n" + code;
}
if (!o.globals && o.scope.hasDeclarations(this)) {
code = "" + this.tab + "var " + (o.scope.compiledDeclarations()) + ";\n" + code;
code = "" + this.tab + "var " + (scope.compiledDeclarations()) + ";\n" + code;
}
return code;
};
Expressions.prototype.compileExpression = function(node, o) {
var code;
o.top = node.tags.front = true;
this.tab = o.indent;
while (node !== (node = node.unwrap())) {
}
node = node.unfoldSoak(o) || node;
node.tags.front = true;
o.level = LVL_TOP;
code = node.compile(o);
return node.isStatement(o) ? code : this.tab + code + ';';
};
@ -262,19 +267,16 @@
Literal.prototype.makeReturn = function() {
return this.isStatement() ? this : Literal.__super__.makeReturn.call(this);
};
Literal.prototype.isStatement = function() {
Literal.prototype.isPureStatement = function() {
var _ref2;
return (_ref2 = this.value) === 'break' || _ref2 === 'continue' || _ref2 === 'debugger';
};
Literal.prototype.isPureStatement = Literal.prototype.isStatement;
Literal.prototype.isComplex = NO;
Literal.prototype.assigns = function(name) {
return name === this.value;
};
Literal.prototype.compileNode = function() {
var val;
val = this.value.reserved ? "\"" + this.value + "\"" : this.value;
return this.isStatement() ? this.tab + val + ';' : val;
Literal.prototype.compile = function() {
return this.value.reserved ? "\"" + this.value + "\"" : this.value;
};
Literal.prototype.toString = function() {
return ' "' + this.value + '"';
@ -301,15 +303,8 @@
return expr && !(expr instanceof Return) ? expr.compile(o, lvl) : Return.__super__.compile.call(this, o, lvl);
};
Return.prototype.compileNode = function(o) {
var expr;
expr = '';
if (this.expression) {
if (this.expression.isStatement(o)) {
o.asStatement = true;
}
expr = ' ' + this.expression.compile(o, LVL_PAREN);
}
return this.tab + 'return' + expr + ';';
o.level = LVL_PAREN;
return this.tab + ("return" + (this.expression ? ' ' + this.expression.compile(o) : '') + ";");
};
return Return;
})();
@ -365,7 +360,7 @@
return this.properties.length ? this : this.base;
};
Value.prototype.isStatement = function(o) {
return this.base.isStatement(o) && !this.properties.length;
return !this.properties.length && this.base.isStatement(o);
};
Value.prototype.isSimpleNumber = function() {
return this.base instanceof Literal && SIMPLENUM.test(this.base.value);
@ -391,23 +386,17 @@
}
return [base.push(name), new Value(bref || base.base, [nref || name])];
};
Value.prototype.compile = function(o, lvl) {
this.base.tags.front = this.tags.front;
return !o.top || this.properties.length ? Value.__super__.compile.call(this, o, lvl) : this.base.compile(o, lvl);
};
Value.prototype.compileNode = function(o) {
var _i, _len, code, ifn, prop, props;
if (ifn = this.unfoldSoak(o)) {
return ifn.compile(o);
}
var _i, _len, code, prop, props;
this.base.tags.front = this.tags.front;
props = this.properties;
code = this.base.compile(o, props.length && LVL_ACCESS);
code = this.base.compile(o, props.length ? LVL_ACCESS : null);
if (props[0] instanceof Accessor && this.isSimpleNumber()) {
code = "(" + code + ")";
}
for (_i = 0, _len = props.length; _i < _len; _i++) {
prop = props[_i];
(code += prop.compileNode(o));
code += prop.compile(o);
}
return code;
};
@ -451,10 +440,10 @@
return Comment;
})();
__extends(Comment, Base);
Comment.prototype.isStatement = YES;
Comment.prototype.isPureStatement = YES;
Comment.prototype.makeReturn = THIS;
Comment.prototype.compileNode = function(o) {
return this.tab + '/*' + this.comment.replace(/\n/g, '\n' + this.tab) + '*/';
return this.tab + '/*' + multident(this.comment, this.tab) + '*/';
};
return Comment;
})();
@ -543,11 +532,10 @@
return ifn;
};
Call.prototype.compileNode = function(o) {
var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _result, arg, args, ifn;
if (ifn = this.unfoldSoak(o)) {
return ifn.compile(o);
var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _result, arg, args;
if ((_ref2 = this.variable) != null) {
_ref2.tags.front = this.tags.front;
}
(_ref2 = this.variable) != null ? _ref2.tags.front = this.tags.front : undefined;
_ref3 = this.args;
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
arg = _ref3[_i];
@ -555,7 +543,7 @@
return this.compileSplat(o);
}
}
args = (function() {
args = ((function() {
_ref4 = this.args;
_result = [];
for (_j = 0, _len2 = _ref4.length; _j < _len2; _j++) {
@ -563,7 +551,7 @@
_result.push(arg.compile(o, LVL_LIST));
}
return _result;
}).call(this).join(', ');
}).call(this)).join(', ');
return this.isSuper ? this.compileSuper(args, o) : (this.isNew ? 'new ' : '') + this.variable.compile(o, LVL_ACCESS) + ("(" + args + ")");
};
Call.prototype.compileSuper = function(args, o) {
@ -579,11 +567,11 @@
base = Value.wrap(this.variable);
if ((name = base.properties.pop()) && base.isComplex()) {
ref = o.scope.freeVariable('this');
fun = "(" + ref + " = " + (base.compile(o, LVL_ASSIGN)) + ")" + (name.compileNode(o));
fun = "(" + ref + " = " + (base.compile(o, LVL_LIST)) + ")" + (name.compile(o));
} else {
fun = ref = base.compile(o, LVL_ACCESS);
if (name) {
fun += name.compileNode(o);
fun += name.compile(o);
}
}
return "" + fun + ".apply(" + ref + ", " + splatargs + ")";
@ -605,7 +593,7 @@
})();
__extends(Extends, Base);
Extends.prototype.children = ['child', 'parent'];
Extends.prototype.compileNode = function(o) {
Extends.prototype.compile = function(o) {
return new Call(new Value(new Literal(utility('extends'))), [this.child, this.parent]).compile(o);
};
return Extends;
@ -623,7 +611,7 @@
})();
__extends(Accessor, Base);
Accessor.prototype.children = ['name'];
Accessor.prototype.compileNode = function(o) {
Accessor.prototype.compile = function(o) {
var name;
name = this.name.compile(o);
return this.proto + (IS_STRING.test(name) ? "[" + name + "]" : "." + name);
@ -642,7 +630,7 @@
})();
__extends(Index, Base);
Index.prototype.children = ['index'];
Index.prototype.compileNode = function(o) {
Index.prototype.compile = function(o) {
return (this.proto ? '.prototype' : '') + ("[" + (this.index.compile(o, LVL_PAREN)) + "]");
};
Index.prototype.isComplex = function() {
@ -662,8 +650,7 @@
__extends(ObjectLiteral, Base);
ObjectLiteral.prototype.children = ['properties'];
ObjectLiteral.prototype.compileNode = function(o) {
var _i, _len, _ref2, _result, i, indent, join, lastNoncom, nonComments, obj, prop, props, top;
top = del(o, 'top');
var _i, _len, _ref2, _result, i, indent, join, lastNoncom, nonComments, obj, prop, props;
o.indent = this.idt(1);
nonComments = (function() {
_ref2 = this.properties;
@ -779,14 +766,13 @@
return this;
};
Class.prototype.compileNode = function(o) {
var _i, _len, _ref2, _ref3, access, applied, apply, className, constScope, construct, constructor, extension, func, me, pname, prop, props, pvar, ref, returns, val, variable;
var _i, _len, _ref2, _ref3, access, applied, apply, className, constScope, construct, constructor, extension, func, me, pname, prop, props, pvar, ref, val, variable;
variable = this.variable;
if (variable.value === '__temp__') {
variable = new Literal(o.scope.freeVariable('ctor'));
}
extension = this.parent && new Extends(variable, this.parent);
props = new Expressions;
o.top = true;
me = null;
className = variable.compile(o);
constScope = null;
@ -847,13 +833,18 @@
if (me) {
constructor.body.unshift(new Literal("" + me + " = this"));
}
construct = this.idt() + new Assign(variable, constructor).compile(merge(o, {
sharedScope: constScope
})) + ';';
props = !props.empty() ? '\n' + props.compile(o) : '';
extension = extension ? '\n' + this.idt() + extension.compile(o) + ';' : '';
returns = this.returns ? '\n' + new Return(variable).compile(o) : '';
return construct + extension + props + returns;
o.sharedScope = constScope;
construct = this.tab + new Assign(variable, constructor).compile(o) + ';';
if (extension) {
construct += '\n' + this.tab + extension.compile(o) + ';';
}
if (!props.empty()) {
construct += '\n' + props.compile(o);
}
if (this.returns) {
construct += '\n' + new Return(variable).compile(o);
}
return construct;
};
return Class;
})();
@ -872,29 +863,28 @@
Assign.prototype.METHOD_DEF = /^(?:(\S+)\.prototype\.)?([$A-Za-z_][$\w]*)$/;
Assign.prototype.CONDITIONAL = ['||=', '&&=', '?='];
Assign.prototype.children = ['variable', 'value'];
Assign.prototype.topSensitive = true;
Assign.prototype.assigns = function(name) {
return this[this.context === 'object' ? 'value' : 'variable'].assigns(name);
};
Assign.prototype.unfoldSoak = function(o) {
return If.unfoldSoak(o, this, 'variable');
};
Assign.prototype.compileNode = function(o) {
var _ref2, ifn, isValue, match, name, stmt, top, val;
var _ref2, isValue, match, name, val;
if (isValue = this.variable instanceof Value) {
if (this.variable.isArray() || this.variable.isObject()) {
return this.compilePatternMatch(o);
}
if (ifn = If.unfoldSoak(o, this, 'variable')) {
delete o.top;
return ifn.compile(o);
}
if (_ref2 = this.context, __indexOf.call(this.CONDITIONAL, _ref2) >= 0) {
return this.compileConditional(o);
}
}
top = del(o, 'top');
stmt = del(o, 'asStatement');
name = this.variable.compile(o, LVL_ASSIGN);
name = this.variable.compile(o, LVL_LIST);
if (this.value instanceof Code && (match = this.METHOD_DEF.exec(name))) {
this.value.name = match[2];
this.value.klass = match[1];
}
val = this.value.compile(o, LVL_ASSIGN);
val = this.value.compile(o, LVL_LIST);
if (this.context === 'object') {
return "" + name + ": " + val;
}
@ -902,17 +892,11 @@
o.scope.find(name);
}
val = name + (" " + (this.context || '=') + " ") + val;
if (stmt) {
return "" + this.tab + val + ";";
}
return top || o.level <= LVL_ASSIGN ? val : "(" + val + ")";
return o.level <= LVL_LIST ? val : "(" + val + ")";
};
Assign.prototype.compilePatternMatch = function(o) {
var _len, _ref2, _ref3, accessClass, assigns, code, i, idx, isObject, obj, objects, olength, otop, ref, splat, top, val, valVar, value;
top = del(o, 'top');
otop = merge(o, {
top: true
});
var _len, _ref2, _ref3, _ref4, _ref5, accessClass, assigns, code, i, idx, isObject, obj, objects, olength, ref, splat, top, val, valVar, value;
top = o.level === LVL_TOP;
value = this.value;
objects = this.variable.base.objects;
if (!(olength = objects.length)) {
@ -921,15 +905,15 @@
isObject = this.variable.isObject();
if (top && olength === 1 && !((obj = objects[0]) instanceof Splat)) {
if (obj instanceof Assign) {
_ref2 = obj, idx = _ref2.variable.base, obj = _ref2.value;
_ref2 = obj, (_ref3 = _ref2.variable, idx = _ref3.base, _ref3), obj = _ref2.value;
} else {
idx = isObject ? obj.tags["this"] ? obj.properties[0].name : obj : new Literal(0);
}
accessClass = IDENTIFIER.test(idx.value) ? Accessor : Index;
(value = Value.wrap(value)).properties.push(new accessClass(idx));
return new Assign(obj, value).compile(otop);
return new Assign(obj, value).compile(o);
}
valVar = value.compile(o);
valVar = value.compile(o, LVL_LIST);
assigns = [];
splat = false;
if (!IDENTIFIER.test(valVar) || this.variable.assigns(valVar)) {
@ -941,7 +925,7 @@
idx = i;
if (isObject) {
if (obj instanceof Assign) {
_ref3 = obj, idx = _ref3.variable.base, obj = _ref3.value;
_ref4 = obj, (_ref5 = _ref4.variable, idx = _ref5.base, _ref5), obj = _ref4.value;
} else {
idx = obj.tags["this"] ? obj.properties[0].name : obj;
}
@ -959,22 +943,19 @@
}
val = new Value(new Literal(valVar), [new accessClass(idx)]);
}
assigns.push(new Assign(obj, val).compile(otop));
assigns.push(new Assign(obj, val).compile(o, LVL_LIST));
}
if (!top) {
assigns.push(valVar);
}
code = assigns.join(', ');
return top || o.level <= LVL_PAREN ? code : "(" + code + ")";
return o.level < LVL_LIST ? code : "(" + code + ")";
};
Assign.prototype.compileConditional = function(o) {
var _ref2, left, rite;
_ref2 = this.variable.cacheReference(o), left = _ref2[0], rite = _ref2[1];
return new Op(this.context.slice(0, -1), left, new Assign(rite, this.value)).compile(o);
};
Assign.prototype.assigns = function(name) {
return this[this.context === 'object' ? 'value' : 'variable'].assigns(name);
};
return Assign;
})();
exports.Code = (function() {
@ -996,11 +977,9 @@
__extends(Code, Base);
Code.prototype.children = ['params', 'body'];
Code.prototype.compileNode = function(o) {
var _i, _len, _len2, _ref2, _ref3, _result, close, code, comm, empty, func, i, open, param, params, sharedScope, splat, top, value;
var _i, _len, _len2, _ref2, _ref3, _result, close, code, comm, empty, func, i, idt, open, param, params, scope, sharedScope, splat, value;
sharedScope = del(o, 'sharedScope');
top = del(o, 'top');
o.scope = sharedScope || new Scope(o.scope, this.body, this);
o.top = true;
o.scope = scope = sharedScope || new Scope(o.scope, this.body, this);
o.indent = this.idt(1);
empty = this.body.expressions.length === 0;
delete o.bare;
@ -1019,7 +998,7 @@
} else {
if (param.attach) {
value = param.value;
_ref3 = [new Literal(o.scope.freeVariable('arg')), param.splat], param = _ref3[0], param.splat = _ref3[1];
_ref3 = [new Literal(scope.freeVariable('arg')), param.splat], param = _ref3[0], param.splat = _ref3[1];
this.body.unshift(new Assign(new Value(new Literal('this'), [new Accessor(value)]), param));
}
if (param.splat) {
@ -1033,31 +1012,36 @@
}
}
}
o.scope.startLevel();
scope.startLevel();
if (!(empty || this.noReturn)) {
this.body.makeReturn();
}
params = (function() {
_result = [];
for (_i = 0, _len2 = params.length; _i < _len2; _i++) {
param = params[_i];
_result.push(param.compile(o));
_result.push((function() {
scope.parameter(param = param.compile(o));
return param;
})());
}
return _result;
})();
if (!(empty || this.noReturn)) {
this.body.makeReturn();
}
for (_i = 0, _len2 = params.length; _i < _len2; _i++) {
param = params[_i];
o.scope.parameter(param);
}
comm = this.comment ? this.comment.compile(o) + '\n' : '';
if (this.className) {
o.indent = this.idt(2);
}
idt = this.idt(1);
code = this.body.expressions.length ? "\n" + (this.body.compileWithDeclarations(o)) + "\n" : '';
open = this.className ? "(function() {\n" + comm + (this.idt(1)) + "function " + this.className + "(" : "function(";
close = this.className ? "" + (code && this.idt(1)) + "};\n" + (this.idt(1)) + "return " + this.className + ";\n" + this.tab + "})()" : "" + (code && this.tab) + "}";
if (this.className) {
open = "(function() {\n" + comm + idt + "function " + this.className + "(";
close = "" + (code && idt) + "};\n" + idt + "return " + this.className + ";\n" + this.tab + "})()";
} else {
open = "function(";
close = "" + (code && this.tab) + "}";
}
func = "" + open + (params.join(', ')) + ") {" + code + close;
o.scope.endLevel();
scope.endLevel();
if (this.bound) {
return "" + (utility('bind')) + "(" + func + ", " + this.context + ")";
}
@ -1082,7 +1066,7 @@
})();
__extends(Param, Base);
Param.prototype.children = ['name'];
Param.prototype.compileNode = function(o) {
Param.prototype.compile = function(o) {
return this.value.compile(o, LVL_LIST);
};
Param.prototype.toString = function() {
@ -1112,7 +1096,7 @@
Splat.prototype.assigns = function(name) {
return this.name.assigns(name);
};
Splat.prototype.compileNode = function(o) {
Splat.prototype.compile = function(o) {
return this.index != null ? this.compileParam(o) : this.name.compile(o);
};
Splat.prototype.compileParam = function(o) {
@ -1182,7 +1166,6 @@
})();
__extends(While, Base);
While.prototype.children = ['condition', 'guard', 'body'];
While.prototype.topSensitive = true;
While.prototype.isStatement = YES;
While.prototype.addBody = function(body) {
this.body = body;
@ -1193,31 +1176,26 @@
return this;
};
While.prototype.compileNode = function(o) {
var cond, post, pre, rvar, set, top;
top = del(o, 'top') && !this.returns;
var body, code, rvar, set;
o.indent = this.idt(1);
cond = this.condition.compile(o, LVL_PAREN);
o.top = true;
set = '';
if (!top) {
body = this.body;
if (o.level > LVL_TOP || this.returns) {
rvar = o.scope.freeVariable('result');
set = "" + this.tab + rvar + " = [];\n";
if (this.body) {
this.body = Push.wrap(rvar, this.body);
if (body) {
body = Push.wrap(rvar, body);
}
}
pre = "" + set + this.tab + "while (" + cond + ")";
if (this.guard) {
this.body = Expressions.wrap([new If(this.guard, this.body)]);
body = Expressions.wrap([new If(this.guard, body)]);
}
code = set + this.tab + ("while (" + (this.condition.compile(o, LVL_PAREN)) + ") {\n" + (body.compile(o, LVL_TOP)) + "\n" + this.tab + "}");
if (this.returns) {
post = '\n' + new Return(new Literal(rvar)).compile(merge(o, {
indent: this.idt()
}));
} else {
post = '';
o.indent = this.tab;
code += '\n' + new Return(new Literal(rvar)).compile(o);
}
return "" + pre + " {\n" + (this.body.compile(o)) + "\n" + this.tab + "}" + post;
return code;
};
return While;
})();
@ -1278,12 +1256,12 @@
Op.prototype.toString = function(idt) {
return Op.__super__.toString.call(this, idt, this.constructor.name + ' ' + this.operator);
};
Op.prototype.unfoldSoak = function(o) {
var _ref2;
return (_ref2 = this.operator, __indexOf.call(this.MUTATORS, _ref2) >= 0) && If.unfoldSoak(o, this, 'first');
};
Op.prototype.compileNode = function(o) {
var _ref2, ifn;
if (this.isUnary()) {
if ((_ref2 = this.operator, __indexOf.call(this.MUTATORS, _ref2) >= 0) && (ifn = If.unfoldSoak(o, this, 'first'))) {
return ifn.compile(o);
}
return this.compileUnary(o);
}
if (this.isChainable() && this.first.unwrap().isChainable()) {
@ -1309,7 +1287,7 @@
fst = this.first;
ref = fst.compile(o);
}
return new Existence(fst).compile(o) + (" ? " + ref + " : " + (this.second.compile(o, LVL_ASSIGN)));
return new Existence(fst).compile(o) + (" ? " + ref + " : " + (this.second.compile(o, LVL_LIST)));
};
Op.prototype.compileUnary = function(o) {
var _ref2, parts, space;
@ -1394,14 +1372,11 @@
return this;
};
Try.prototype.compileNode = function(o) {
var attemptPart, catchPart, errorPart, finallyPart;
var catchPart, errorPart;
o.indent = this.idt(1);
o.top = true;
attemptPart = this.attempt.compile(o);
errorPart = this.error ? " (" + (this.error.compile(o)) + ") " : ' ';
catchPart = this.recovery ? " catch" + errorPart + "{\n" + (this.recovery.compile(o)) + "\n" + this.tab + "}" : !(this.ensure || this.recovery) ? ' catch (_e) {}' : '';
finallyPart = (this.ensure || '') && ' finally {\n' + this.ensure.compile(merge(o)) + ("\n" + this.tab + "}");
return "" + this.tab + "try {\n" + attemptPart + "\n" + this.tab + "}" + catchPart + finallyPart;
catchPart = this.recovery ? " catch" + errorPart + "{\n" + (this.recovery.compile(o, LVL_TOP)) + "\n" + this.tab + "}" : !(this.ensure || this.recovery) ? ' catch (_e) {}' : undefined;
return ("" + this.tab + "try {\n" + (this.attempt.compile(o, LVL_TOP)) + "\n" + this.tab + "}" + (catchPart || '')) + (this.ensure ? " finally {\n" + (this.ensure.compile(o, LVL_TOP)) + "\n" + this.tab + "}" : '');
};
return Try;
})();
@ -1419,7 +1394,7 @@
Throw.prototype.isStatement = YES;
Throw.prototype.makeReturn = THIS;
Throw.prototype.compileNode = function(o) {
return "" + this.tab + "throw " + (this.expression.compile(o)) + ";";
return this.tab + ("throw " + (this.expression.compile(o)) + ";");
};
return Throw;
})();
@ -1453,9 +1428,8 @@
})();
__extends(Parens, Base);
Parens.prototype.children = ['expression'];
Parens.prototype.topSensitive = true;
Parens.prototype.isStatement = function(o) {
return this.expression.isStatement(o);
Parens.prototype.unwrap = function() {
return this.expression;
};
Parens.prototype.isComplex = function() {
return this.expression.isComplex();
@ -1464,21 +1438,15 @@
return this.expression.makeReturn();
};
Parens.prototype.compileNode = function(o) {
var code, expr, top;
top = del(o, 'top');
var bare, code, expr;
expr = this.expression;
if (expr instanceof Value && expr.isAtomic()) {
expr.tags.front = this.tags.front;
return expr.compile(o);
}
bare = o.level < LVL_OP && (expr instanceof Op || expr instanceof Call);
code = expr.compile(o, LVL_PAREN);
if (o.level < LVL_OP && (expr instanceof Op || expr instanceof Call)) {
return code;
}
if (expr.isStatement(o)) {
return (top ? this.tab + code + ';' : code);
}
return "(" + code + ")";
return bare ? code : "(" + code + ")";
};
return Parens;
})();
@ -1505,7 +1473,6 @@
})();
__extends(For, Base);
For.prototype.children = ['body', 'source', 'guard', 'step', 'from', 'to'];
For.prototype.topSensitive = true;
For.prototype.isStatement = YES;
For.prototype.makeReturn = function() {
this.returns = true;
@ -1521,9 +1488,8 @@
return '';
};
For.prototype.compileNode = function(o) {
var _ref2, _ref3, _ref4, _ref5, _ref6, body, cond, defPart, forPart, guardPart, idt, index, ivar, lvar, name, namePart, pvar, resultRet, rvar, scope, sourcePart, step, svar, tail, top, tvar, varPart, vars;
var _ref2, _ref3, _ref4, _ref5, _ref6, body, cond, defPart, forPart, guardPart, idt, index, ivar, lvar, name, namePart, pvar, resultRet, rvar, scope, sourcePart, step, svar, tail, tvar, varPart, vars;
scope = o.scope;
top = del(o, 'top') && !this.returns;
name = !this.pattern && ((_ref2 = this.name) != null ? _ref2.compile(o) : undefined);
index = (_ref3 = this.index) != null ? _ref3.compile(o) : undefined;
ivar = !index ? scope.freeVariable('i') : index;
@ -1554,11 +1520,9 @@
if (name) {
_ref6 = this.source.compileLoopReference(o, 'ref'), sourcePart = _ref6[0], svar = _ref6[1];
} else {
sourcePart = svar = this.source.compile(o);
sourcePart = svar = this.source.compile(o, LVL_PAREN);
}
namePart = this.pattern ? new Assign(this.name, new Literal("" + svar + "[" + ivar + "]")).compile(merge(o, {
top: true
})) : name ? "" + name + " = " + svar + "[" + ivar + "]" : undefined;
namePart = this.pattern ? new Assign(this.name, new Literal("" + svar + "[" + ivar + "]")).compile(o, LVL_TOP) : name ? "" + name + " = " + svar + "[" + ivar + "]" : undefined;
if (!this.object) {
if (0 > pvar && (pvar | 0) === +pvar) {
vars = "" + ivar + " = " + svar + ".length - 1";
@ -1581,18 +1545,18 @@
if (svar !== sourcePart) {
defPart = "" + this.tab + sourcePart + ";\n";
}
forPart = ("" + vars + "; " + cond + "; ") + (function() {
forPart = ("" + vars + "; " + cond + "; ") + ivar + (function() {
switch (+pvar) {
case 1:
return ivar + '++';
return '++';
case -1:
return ivar + '--';
return '--';
default:
return ivar + (pvar < 0 ? ' -= ' + pvar.slice(1) : ' += ' + pvar);
return pvar < 0 ? ' -= ' + pvar.slice(1) : ' += ' + pvar;
}
})();
}
if (!top) {
if (o.level > LVL_TOP || this.returns) {
rvar = scope.freeVariable('result');
defPart += "" + this.tab + rvar + " = [];\n";
resultRet = this.compileReturnValue(rvar, o);
@ -1604,10 +1568,8 @@
if (namePart) {
varPart = "" + idt + namePart + ";\n";
}
return "" + (defPart || '') + this.tab + "for (" + forPart + ") {\n" + (guardPart || '') + varPart + (body.compile(merge(o, {
indent: idt,
top: true
}))) + "\n" + this.tab + "}" + (resultRet || '');
o.indent = idt;
return "" + (defPart || '') + this.tab + "for (" + forPart + ") {\n" + (guardPart || '') + varPart + (body.compile(o, LVL_TOP)) + "\n" + this.tab + "}" + (resultRet || '');
};
return For;
})();
@ -1626,43 +1588,51 @@
Switch.prototype.children = ['subject', 'cases', 'otherwise'];
Switch.prototype.isStatement = YES;
Switch.prototype.makeReturn = function() {
var _i, _len, _ref2, pair;
var _i, _len, _ref2, _ref3, pair;
_ref2 = this.cases;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
pair = _ref2[_i];
pair[1].makeReturn();
}
if (this.otherwise) {
this.otherwise.makeReturn();
if ((_ref3 = this.otherwise) != null) {
_ref3.makeReturn();
}
return this;
};
Switch.prototype.compileNode = function(o) {
var _i, _j, _len, _len2, _ref2, _ref3, _ref4, block, code, condition, conditions, idt1, idt2;
var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _ref5, block, code, cond, conditions, expr, i, idt1, idt2;
idt1 = this.idt(1);
idt2 = o.indent = this.idt(2);
o.top = true;
code = "" + this.tab + "switch (" + (((_ref2 = this.subject) != null ? _ref2.compile(o) : undefined) || true) + ") {";
for (_i = 0, _len = this.cases.length; _i < _len; _i++) {
_ref3 = this.cases[_i], conditions = _ref3[0], block = _ref3[1];
code = this.tab + ("switch (" + (((_ref2 = this.subject) != null ? _ref2.compile(o, LVL_PAREN) : undefined) || true) + ") {\n");
for (i = 0, _len = this.cases.length; i < _len; i++) {
_ref3 = this.cases[i], conditions = _ref3[0], block = _ref3[1];
_ref4 = flatten([conditions]);
for (_j = 0, _len2 = _ref4.length; _j < _len2; _j++) {
condition = _ref4[_j];
for (_i = 0, _len2 = _ref4.length; _i < _len2; _i++) {
cond = _ref4[_i];
if (!this.subject) {
condition = condition.invert().invert();
cond = cond.invert().invert();
}
code += "\n" + idt1 + "case " + (condition.compile(o)) + ":";
code += idt1 + ("case " + (cond.compile(o, LVL_PAREN)) + ":\n");
}
code += "\n" + (block.compile(o));
if (!(last(block.expressions) instanceof Return)) {
code += "\n" + idt2 + "break;";
code += block.compile(o, LVL_TOP) + '\n';
if (i === this.cases.length - 1 && !this.otherwise) {
break;
}
_ref5 = block.expressions;
for (_j = _ref5.length - 1; _j >= 0; _j--) {
expr = _ref5[_j];
if (!(expr instanceof Comment)) {
if (!(expr instanceof Return)) {
code += idt2 + 'break;\n';
}
break;
}
}
}
if (this.otherwise) {
code += "\n" + idt1 + "default:\n" + (this.otherwise.compile(o));
code += idt1 + ("default:\n" + (this.otherwise.compile(o, LVL_TOP)) + "\n");
}
code += "\n" + this.tab + "}";
return code;
return code + this.tab + '}';
};
return Switch;
})();
@ -1681,7 +1651,6 @@
})();
__extends(If, Base);
If.prototype.children = ['condition', 'body', 'elseBody'];
If.prototype.topSensitive = true;
If.prototype.bodyNode = function() {
var _ref2;
return (_ref2 = this.body) != null ? _ref2.unwrap() : undefined;
@ -1701,7 +1670,7 @@
};
If.prototype.isStatement = function(o) {
var _ref2;
return this.statement || (this.statement = (o != null ? o.top : undefined) || this.bodyNode().isStatement(o) || ((_ref2 = this.elseBodyNode()) != null ? _ref2.isStatement(o) : undefined));
return (o != null ? o.level : undefined) === LVL_TOP || this.bodyNode().isStatement(o) || ((_ref2 = this.elseBodyNode()) != null ? _ref2.isStatement(o) : undefined);
};
If.prototype.compileNode = function(o) {
return this.isStatement(o) ? this.compileStatement(o) : this.compileExpression(o);
@ -1719,13 +1688,12 @@
return node instanceof Expressions ? node : new Expressions([node]);
};
If.prototype.compileStatement = function(o) {
var child, condO, ifPart, top;
top = del(o, 'top');
var body, child, cond, ifPart;
child = del(o, 'chainChild');
condO = merge(o);
cond = this.condition.compile(o, LVL_PAREN);
o.indent = this.idt(1);
o.top = true;
ifPart = "if (" + (this.condition.compile(condO, LVL_PAREN)) + ") {\n" + (this.body.compile(o)) + "\n" + this.tab + "}";
body = this.ensureExpressions(this.body).compile(o);
ifPart = "if (" + cond + ") {\n" + body + "\n" + this.tab + "}";
if (!child) {
ifPart = this.tab + ifPart;
}
@ -1735,11 +1703,11 @@
return ifPart + ' else ' + (this.isChain ? this.elseBodyNode().compile(merge(o, {
indent: this.tab,
chainChild: true
})) : "{\n" + (this.elseBody.compile(o)) + "\n" + this.tab + "}");
})) : "{\n" + (this.elseBody.compile(o, LVL_TOP)) + "\n" + this.tab + "}");
};
If.prototype.compileExpression = function(o) {
var _ref2, code;
code = this.condition.compile(o, LVL_COND) + ' ? ' + this.bodyNode().compile(o, LVL_ASSIGN) + ' : ' + ((_ref2 = this.elseBodyNode()) != null ? _ref2.compile(o, LVL_ASSIGN) : undefined);
code = this.condition.compile(o, LVL_COND) + ' ? ' + this.bodyNode().compile(o, LVL_LIST) + ' : ' + ((_ref2 = this.elseBodyNode()) != null ? _ref2.compile(o, LVL_LIST) : undefined);
return o.level >= LVL_COND ? "(" + code + ")" : code;
};
If.prototype.unfoldSoak = function() {
@ -1801,10 +1769,9 @@
LVL_TOP = 0;
LVL_PAREN = 1;
LVL_LIST = 2;
LVL_ASSIGN = 3;
LVL_COND = 4;
LVL_OP = 5;
LVL_ACCESS = 6;
LVL_COND = 3;
LVL_OP = 4;
LVL_ACCESS = 5;
TAB = ' ';
TRAILING_WHITESPACE = /[ \t]+$/gm;
IDENTIFIER = /^[$A-Za-z_][$\w]*$/;
@ -1817,4 +1784,7 @@
Scope.root.assign(ref, UTILITIES[name]);
return ref;
};
multident = function(code, tab) {
return code.replace(/\n/g, '$&' + tab);
};
}).call(this);

View File

@ -30,7 +30,7 @@
tokens = this.tokens;
i = 0;
while (token = tokens[i]) {
(i += block.call(this, token, i, tokens));
i += block.call(this, token, i, tokens);
}
return true;
};
@ -322,7 +322,7 @@
stack = [];
debt = {};
for (key in INVERSES) {
(debt[key] = 0);
debt[key] = 0;
}
return this.scanTokens(function(token, i, tokens) {
var _ref, inv, match, mtag, oppos, tag, val;

View File

@ -101,7 +101,9 @@
index++;
}
this.add(temp, 'var');
(_ref2 = last(this.garbage)) != null ? _ref2.push(temp) : undefined;
if ((_ref2 = last(this.garbage)) != null) {
_ref2.push(temp);
}
return temp;
};
Scope.prototype.assign = function(name, value) {
@ -123,7 +125,7 @@
};
Scope.prototype.declaredVariables = function() {
var _i, _len, _ref2, _ref3, _result, v;
return (function() {
return ((function() {
_ref2 = this.variables;
_result = [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
@ -133,7 +135,7 @@
}
}
return _result;
}).call(this).sort();
}).call(this)).sort();
};
Scope.prototype.assignedVariables = function() {
var _i, _len, _ref2, _result, v;

View File

@ -37,20 +37,15 @@ exports.Base = class Base
# the top level of a block (which would be unnecessary), and we haven't
# already been asked to return the result (because statements know how to
# return results).
#
# If a Node is *topSensitive*, that means that it needs to compile differently
# depending on whether it's being used as part of a larger expression, or is a
# top-level statement within the function body.
compile: (o, lvl) ->
o = if o then extend {}, o else {}
o.level = lvl if lvl
top = if @topSensitive then o.top else del o, 'top'
@tab = o.indent
if top or o.asStatement or this instanceof Comment or
@isPureStatement() or not @isStatement(o)
@compileNode o
o = if o then extend {}, o else {}
o.level = lvl if lvl?
node = @unfoldSoak(o) or this
node.tab = o.indent
if o.level is LVL_TOP or node.isPureStatement() or not node.isStatement(o)
node.compileNode o
else
@compileClosure o
node.compileClosure o
# Statements converted into expressions via closure-wrapping share a scope
# object with their parent closure, to preserve the expected lexical scope.
@ -74,7 +69,7 @@ exports.Base = class Base
# Compile to a source/variable pair suitable for looping.
compileLoopReference: (o, name) ->
src = tmp = @compile o, LVL_ASSIGN
src = tmp = @compile o, LVL_LIST
unless NUMBER.test(src) or IDENTIFIER.test(src) and o.scope.check(src, immediate: on)
src = "#{ tmp = o.scope.freeVariable name } = #{src}"
[src, tmp]
@ -147,7 +142,6 @@ exports.Base = class Base
isPureStatement : NO
isComplex : YES
isChainable : NO
topSensitive : no
unfoldSoak : NO
# Is this node used to assign a certain variable?
@ -162,7 +156,7 @@ exports.Expressions = class Expressions extends Base
children: ['expressions']
isStatement: YES
isStatement: YES
constructor: (nodes) ->
super()
@ -170,12 +164,12 @@ exports.Expressions = class Expressions extends Base
# Tack an expression on to the end of this expression list.
push: (node) ->
@expressions.push(node)
@expressions.push node
this
# Add an expression at the beginning of this expression list.
unshift: (node) ->
@expressions.unshift(node)
@expressions.unshift node
this
# If this Expressions consists of just a single node, unwrap it by pulling
@ -201,7 +195,8 @@ exports.Expressions = class Expressions extends Base
if o.scope then super o, lvl else @compileRoot o
compileNode: (o) ->
(@compileExpression node, merge o for node in @expressions).join '\n'
@tab = o.indent
(@compileExpression node, o for node in @expressions).join '\n'
# If we happen to be the top-level **Expressions**, wrap everything in
# a safety closure, unless requested not to.
@ -218,20 +213,23 @@ exports.Expressions = class Expressions extends Base
# Compile the expressions body for the contents of a function, with
# declarations of all inner variables pushed up to the top.
compileWithDeclarations: (o) ->
code = @compileNode o
if o.scope.hasAssignments this
code = "#{@tab}var #{ o.scope.compiledAssignments().replace /\n/g, '$&' + @tab };\n#{code}"
code = @compileNode o
{scope} = o
if scope.hasAssignments this
code = "#{@tab}var #{ multident scope.compiledAssignments(), @tab };\n#{code}"
if not o.globals and o.scope.hasDeclarations this
code = "#{@tab}var #{ o.scope.compiledDeclarations() };\n#{code}"
code = "#{@tab}var #{ scope.compiledDeclarations() };\n#{code}"
code
# Compiles a single expression within the expressions body. If we need to
# return the result, and it's an expression, simply return it. If it's a
# statement, ask the statement to do so.
compileExpression: (node, o) ->
o.top = node.tags.front = on
@tab = o.indent
code = node.compile o
while node isnt node = node.unwrap() then
node = node.unfoldSoak(o) or node
node.tags.front = on
o.level = LVL_TOP
code = node.compile o
if node.isStatement o then code else @tab + code + ';'
# Wrap up the given nodes as an **Expressions**, unless it already happens
@ -247,24 +245,19 @@ exports.Expressions = class Expressions extends Base
# `true`, `false`, `null`...
exports.Literal = class Literal extends Base
constructor: (@value) ->
super()
constructor: (@value) -> super()
makeReturn: ->
if @isStatement() then this else super()
makeReturn: -> if @isStatement() then this else super()
# Break and continue must be treated as pure statements -- they lose their
# meaning when wrapped in a closure.
isStatement : -> @value in ['break', 'continue', 'debugger']
isPureStatement: Literal::isStatement
isPureStatement: -> @value in ['break', 'continue', 'debugger']
isComplex: NO
assigns: (name) -> name is @value
compileNode: ->
val = if @value.reserved then "\"#{@value}\"" else @value
if @isStatement() then @tab + val + ';' else val
compile: -> if @value.reserved then "\"#{@value}\"" else @value
toString: -> ' "' + @value + '"'
@ -279,8 +272,7 @@ exports.Return = class Return extends Base
isStatement : YES
isPureStatement: YES
constructor: (@expression) ->
super()
constructor: (@expression) -> super()
makeReturn: THIS
@ -289,11 +281,8 @@ exports.Return = class Return extends Base
if expr and expr not instanceof Return then expr.compile o, lvl else super o, lvl
compileNode: (o) ->
expr = ''
if @expression
o.asStatement = true if @expression.isStatement o
expr = ' ' + @expression.compile o, LVL_PAREN
@tab + 'return' + expr + ';'
o.level = LVL_PAREN
@tab + "return#{ if @expression then ' ' + @expression.compile o else '' };"
#### Value
@ -311,7 +300,7 @@ exports.Value = class Value extends Base
# Add a property access to the list.
push: (prop) ->
@properties.push(prop)
@properties.push prop
this
hasProperties: ->
@ -346,7 +335,7 @@ exports.Value = class Value extends Base
# Values are considered to be statements if their base is a statement.
isStatement: (o) ->
@base.isStatement(o) and not @properties.length
not @properties.length and @base.isStatement o
isSimpleNumber: ->
@base instanceof Literal and SIMPLENUM.test @base.value
@ -369,21 +358,16 @@ exports.Value = class Value extends Base
nref = new Index nref
[base.push(name), new Value(bref or base.base, [nref or name])]
# Override compile to unwrap the value when possible.
compile: (o, lvl) ->
@base.tags.front = @tags.front
if not o.top or @properties.length then super o, lvl else @base.compile o, lvl
# We compile a value to JavaScript by compiling and joining each property.
# Things get much more insteresting if the chain of properties has *soak*
# operators `?.` interspersed. Then we have to take care not to accidentally
# evaluate anything twice when building the soak chain.
compileNode: (o) ->
return ifn.compile o if ifn = @unfoldSoak o
@base.tags.front = @tags.front
props = @properties
code = @base.compile o, props.length and LVL_ACCESS
code = @base.compile o, if props.length then LVL_ACCESS else null
code = "(#{code})" if props[0] instanceof Accessor and @isSimpleNumber()
(code += prop.compileNode o) for prop in props
(code += prop.compile o) for prop in props
code
# Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
@ -410,15 +394,13 @@ exports.Value = class Value extends Base
# at the same position.
exports.Comment = class Comment extends Base
isStatement: YES
isPureStatement: YES
constructor: (@comment) ->
super()
constructor: (@comment) -> super()
makeReturn: THIS
compileNode: (o) ->
@tab + '/*' + @comment.replace(/\n/g, '\n' + @tab) + '*/'
compileNode: (o) -> @tab + '/*' + multident(@comment, @tab) + '*/'
#### Call
@ -488,7 +470,6 @@ exports.Call = class Call extends Base
# Compile a vanilla function call.
compileNode: (o) ->
return ifn.compile o if ifn = @unfoldSoak o
@variable?.tags.front = @tags.front
for arg in @args when arg instanceof Splat
return @compileSplat o
@ -514,10 +495,10 @@ exports.Call = class Call extends Base
base = Value.wrap @variable
if (name = base.properties.pop()) and base.isComplex()
ref = o.scope.freeVariable 'this'
fun = "(#{ref} = #{ base.compile o, LVL_ASSIGN })#{ name.compileNode o }"
fun = "(#{ref} = #{ base.compile o, LVL_LIST })#{ name.compile o }"
else
fun = ref = base.compile o, LVL_ACCESS
fun += name.compileNode o if name
fun += name.compile o if name
return "#{fun}.apply(#{ref}, #{splatargs})"
idt = @idt 1
"""
@ -537,11 +518,10 @@ exports.Extends = class Extends extends Base
children: ['child', 'parent']
constructor: (@child, @parent) ->
super()
constructor: (@child, @parent) -> super()
# Hooks one constructor into another's prototype chain.
compileNode: (o) ->
compile: (o) ->
new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compile o
#### Accessor
@ -557,7 +537,7 @@ exports.Accessor = class Accessor extends Base
@proto = if tag is 'prototype' then '.prototype' else ''
@soakNode = tag is 'soak'
compileNode: (o) ->
compile: (o) ->
name = @name.compile o
@proto + if IS_STRING.test name then "[#{name}]" else ".#{name}"
@ -570,10 +550,9 @@ exports.Index = class Index extends Base
children: ['index']
constructor: (@index) ->
super()
constructor: (@index) -> super()
compileNode: (o) ->
compile: (o) ->
(if @proto then '.prototype' else '') + "[#{ @index.compile o, LVL_PAREN }]"
isComplex: -> @index.isComplex()
@ -590,7 +569,6 @@ exports.ObjectLiteral = class ObjectLiteral extends Base
@objects = @properties = props or []
compileNode: (o) ->
top = del o, 'top'
o.indent = @idt 1
nonComments = prop for prop in @properties when prop not instanceof Comment
lastNoncom = last nonComments
@ -660,7 +638,7 @@ exports.Class = class Class extends Base
children: ['variable', 'parent', 'properties']
isStatement: YES
isStatement: YES
# Initialize a **Class** with its name, an optional superclass, and a
# list of prototype property assignments.
@ -682,7 +660,6 @@ exports.Class = class Class extends Base
variable = new Literal o.scope.freeVariable 'ctor' if variable.value is '__temp__'
extension = @parent and new Extends variable, @parent
props = new Expressions
o.top = true
me = null
className = variable.compile o
constScope = null
@ -730,11 +707,12 @@ exports.Class = class Class extends Base
constructor.className = className.match /[$\w]+$/
constructor.body.unshift new Literal "#{me} = this" if me
construct = @idt() + new Assign(variable, constructor).compile(merge o, sharedScope: constScope) + ';'
props = if !props.empty() then '\n' + props.compile(o) else ''
extension = if extension then '\n' + @idt() + extension.compile(o) + ';' else ''
returns = if @returns then '\n' + new Return(variable).compile(o) else ''
construct + extension + props + returns
o.sharedScope = constScope
construct = @tab + new Assign(variable, constructor).compile(o) + ';'
construct += '\n' + @tab + extension.compile(o) + ';' if extension
construct += '\n' + props.compile o if !props.empty()
construct += '\n' + new Return(variable).compile o if @returns
construct
#### Assign
@ -749,10 +727,12 @@ exports.Assign = class Assign extends Base
children: ['variable', 'value']
topSensitive: yes
constructor: (@variable, @value, @context) -> super()
constructor: (@variable, @value, @context) ->
super()
assigns: (name) ->
@[if @context is 'object' then 'value' else 'variable'].assigns name
unfoldSoak: (o) -> If.unfoldSoak o, this, 'variable'
# Compile an assignment, delegating to `compilePatternMatch` or
# `compileSplice` if appropriate. Keep track of the name of the base object
@ -760,31 +740,24 @@ exports.Assign = class Assign extends Base
# has not been seen yet within the current scope, declare it.
compileNode: (o) ->
if isValue = @variable instanceof Value
return @compilePatternMatch(o) if @variable.isArray() or @variable.isObject()
if ifn = If.unfoldSoak o, this, 'variable'
delete o.top
return ifn.compile o
return @compileConditional o if @context in @CONDITIONAL
top = del o, 'top'
stmt = del o, 'asStatement'
name = @variable.compile o, LVL_ASSIGN
return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
return @compileConditional o if @context in @CONDITIONAL
name = @variable.compile o, LVL_LIST
if @value instanceof Code and match = @METHOD_DEF.exec name
@value.name = match[2]
@value.klass = match[1]
val = @value.compile o, LVL_ASSIGN
val = @value.compile o, LVL_LIST
return "#{name}: #{val}" if @context is 'object'
o.scope.find name unless isValue and (@variable.hasProperties() or @variable.namespaced)
val = name + " #{ @context or '=' } " + val
return "#{@tab}#{val};" if stmt
if top or o.level <= LVL_ASSIGN then val else "(#{val})"
if o.level <= LVL_LIST then val else "(#{val})"
# Brief implementation of recursive pattern matching, when assigning array or
# object literals to a value. Peeks at their properties to assign inner names.
# See the [ECMAScript Harmony Wiki](http://wiki.ecmascript.org/doku.php?id=harmony:destructuring)
# for details.
compilePatternMatch: (o) ->
top = del o, 'top'
otop = merge o, top: yes
top = o.level is LVL_TOP
{value} = this
{objects} = @variable.base
return value.compile o unless olength = objects.length
@ -796,11 +769,12 @@ exports.Assign = class Assign extends Base
else
idx = if isObject
if obj.tags.this then obj.properties[0].name else obj
else new Literal 0
else
new Literal 0
accessClass = if IDENTIFIER.test idx.value then Accessor else Index
(value = Value.wrap value).properties.push new accessClass idx
return new Assign(obj, value).compile otop
valVar = value.compile o
return new Assign(obj, value).compile o
valVar = value.compile o, LVL_LIST
assigns = []
splat = false
if not IDENTIFIER.test(valVar) or @variable.assigns(valVar)
@ -823,12 +797,13 @@ exports.Assign = class Assign extends Base
val = new Literal obj.compileValue o, valVar, i, olength - i - 1
splat = true
else
idx = new Literal(if splat then "#{valVar}.length - #{olength - idx}" else idx) if typeof idx isnt 'object'
if typeof idx isnt 'object'
idx = new Literal(if splat then "#{valVar}.length - #{ olength - idx }" else idx)
val = new Value new Literal(valVar), [new accessClass idx]
assigns.push new Assign(obj, val).compile otop
assigns.push new Assign(obj, val).compile o, LVL_LIST
assigns.push valVar unless top
code = assigns.join ', '
if top or o.level <= LVL_PAREN then code else "(#{code})"
if o.level < LVL_LIST then code else "(#{code})"
# When compiling a conditional assignment, take care to ensure that the
# operands are only evaluated once, even though we have to reference them
@ -837,9 +812,6 @@ exports.Assign = class Assign extends Base
[left, rite] = @variable.cacheReference o
return new Op(@context.slice(0, -1), left, new Assign(rite, @value)).compile o
assigns: (name) ->
@[if @context is 'object' then 'value' else 'variable'].assigns name
#### Code
# A function definition. This is the only node that creates a new Scope.
@ -863,9 +835,7 @@ exports.Code = class Code extends Base
# a closure.
compileNode: (o) ->
sharedScope = del o, 'sharedScope'
top = del o, 'top'
o.scope = sharedScope or new Scope(o.scope, @body, this)
o.top = true
o.scope = scope = sharedScope or new Scope o.scope, @body, this
o.indent = @idt 1
empty = @body.expressions.length is 0
delete o.bare
@ -881,28 +851,34 @@ exports.Code = class Code extends Base
else
if param.attach
{value} = param
[param, param.splat] = [new Literal(o.scope.freeVariable 'arg'), param.splat]
[param, param.splat] = [new Literal(scope.freeVariable 'arg'), param.splat]
@body.unshift new Assign new Value(new Literal('this'), [new Accessor value]), param
if param.splat
splat = new Splat param.value
splat.index = i
splat.trailings = []
splat.arglength = @params.length
@body.unshift(splat)
@body.unshift splat
else
params.push param
o.scope.startLevel()
params = param.compile(o) for param in params
scope.startLevel()
@body.makeReturn() unless empty or @noReturn
o.scope.parameter param for param in params
params = for param in params
scope.parameter param = param.compile o
param
comm = if @comment then @comment.compile(o) + '\n' else ''
o.indent = @idt 2 if @className
code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else ''
open = if @className then "(function() {\n#{comm}#{@idt(1)}function #{@className}(" else "function("
close = if @className then "#{code and @idt(1)}};\n#{@idt(1)}return #{@className};\n#{@tab}})()" else "#{code and @tab}}"
func = "#{open}#{ params.join(', ') }) {#{code}#{close}"
o.scope.endLevel()
return "#{utility 'bind'}(#{func}, #{@context})" if @bound
idt = @idt 1
code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations o }\n" else ''
if @className
open = "(function() {\n#{comm}#{idt}function #{@className}("
close = "#{ code and idt }};\n#{idt}return #{@className};\n#{@tab}})()"
else
open = "function("
close = "#{ code and @tab }}"
func = "#{open}#{ params.join ', ' }) {#{code}#{close}"
scope.endLevel()
return "#{ utility 'bind' }(#{func}, #{@context})" if @bound
if @tags.front then "(#{func})" else func
# Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
@ -922,13 +898,12 @@ exports.Param = class Param extends Base
super()
@value = new Literal @name
compileNode: (o) ->
@value.compile o, LVL_LIST
compile: (o) -> @value.compile o, LVL_LIST
toString: ->
{name} = @
name = '@' + name if @attach
name += '...' if @splat
{name} = this
name = '@' + name if @attach
name += '...' if @splat
new Literal(name).toString()
#### Splat
@ -945,7 +920,7 @@ exports.Splat = class Splat extends Base
assigns: (name) -> @name.assigns name
compileNode: (o) ->
compile: (o) ->
if @index? then @compileParam o else @name.compile o
# Compiling a parameter splat means recovering the parameters that succeed
@ -1004,8 +979,7 @@ exports.While = class While extends Base
children: ['condition', 'guard', 'body']
topSensitive: yes
isStatement : YES
isStatement: YES
constructor: (condition, opts) ->
super()
@ -1024,22 +998,23 @@ exports.While = class While extends Base
# *while* can be used as a part of a larger expression -- while loops may
# return an array containing the computed result of each iteration.
compileNode: (o) ->
top = del(o, 'top') and not @returns
o.indent = @idt 1
cond = @condition.compile o, LVL_PAREN
o.top = true
set = ''
unless top
rvar = o.scope.freeVariable 'result'
set = "#{@tab}#{rvar} = [];\n"
@body = Push.wrap(rvar, @body) if @body
pre = "#{set}#{@tab}while (#{cond})"
@body = Expressions.wrap [new If @guard, @body] if @guard
{body} = this
if o.level > LVL_TOP or @returns
rvar = o.scope.freeVariable 'result'
set = "#{@tab}#{rvar} = [];\n"
body = Push.wrap rvar, body if body
body = Expressions.wrap [new If @guard, body] if @guard
code = set + @tab + """
while (#{ @condition.compile o, LVL_PAREN }) {
#{ body.compile o, LVL_TOP }
#{@tab}}
"""
if @returns
post = '\n' + new Return(new Literal rvar).compile merge o, indent: @idt()
else
post = ''
"#{pre} {\n#{ @body.compile(o) }\n#{@tab}}#{post}"
o.indent = @tab
code += '\n' + new Return(new Literal rvar).compile o
code
#### Op
@ -1102,9 +1077,11 @@ exports.Op = class Op extends Base
toString: (idt) ->
super idt, @constructor.name + ' ' + @operator
unfoldSoak: (o) ->
@operator in @MUTATORS and If.unfoldSoak o, this, 'first'
compileNode: (o) ->
if @isUnary()
return ifn.compile o if @operator in @MUTATORS and ifn = If.unfoldSoak o, this, 'first'
return @compileUnary o
return @compileChain o if @isChainable() and @first.unwrap().isChainable()
return @compileExistence o if @operator is '?'
@ -1127,7 +1104,7 @@ exports.Op = class Op extends Base
else
fst = @first
ref = fst.compile o
new Existence(fst).compile(o) + " ? #{ref} : #{ @second.compile o, LVL_ASSIGN }"
new Existence(fst).compile(o) + " ? #{ref} : #{ @second.compile o, LVL_LIST }"
# Compile a unary **Op**.
compileUnary: (o) ->
@ -1140,8 +1117,7 @@ exports.In = class In extends Base
children: ['object', 'array']
constructor: (@object, @array) ->
super()
constructor: (@object, @array) -> super()
invert: ->
@negated = not @negated
@ -1181,8 +1157,7 @@ exports.Try = class Try extends Base
isStatement: YES
constructor: (@attempt, @error, @recovery, @ensure) ->
super()
constructor: (@attempt, @error, @recovery, @ensure) -> super()
makeReturn: ->
@attempt = @attempt .makeReturn() if @attempt
@ -1192,15 +1167,17 @@ exports.Try = class Try extends Base
# Compilation is more or less as you would expect -- the *finally* clause
# is optional, the *catch* is not.
compileNode: (o) ->
o.indent = @idt 1
o.top = true
attemptPart = @attempt.compile(o)
errorPart = if @error then " (#{ @error.compile o }) " else ' '
catchPart = if @recovery
" catch#{errorPart}{\n#{ @recovery.compile o }\n#{@tab}}"
else unless @ensure or @recovery then ' catch (_e) {}' else ''
finallyPart = (@ensure or '') and ' finally {\n' + @ensure.compile(merge o) + "\n#{@tab}}"
"#{@tab}try {\n#{attemptPart}\n#{@tab}}#{catchPart}#{finallyPart}"
o.indent = @idt 1
errorPart = if @error then " (#{ @error.compile o }) " else ' '
catchPart = if @recovery
" catch#{errorPart}{\n#{ @recovery.compile o, LVL_TOP }\n#{@tab}}"
else unless @ensure or @recovery
' catch (_e) {}'
"""
#{@tab}try {
#{ @attempt.compile o, LVL_TOP }
#{@tab}}#{ catchPart or '' }
""" + if @ensure then " finally {\n#{ @ensure.compile o, LVL_TOP }\n#{@tab}}" else ''
#### Throw
@ -1211,14 +1188,12 @@ exports.Throw = class Throw extends Base
isStatement: YES
constructor: (@expression) ->
super()
constructor: (@expression) -> super()
# A **Throw** is already a return, of sorts...
makeReturn: THIS
compileNode: (o) ->
"#{@tab}throw #{ @expression.compile o };"
compileNode: (o) -> @tab + "throw #{ @expression.compile o };"
#### Existence
@ -1229,8 +1204,7 @@ exports.Existence = class Existence extends Base
children: ['expression']
constructor: (@expression) ->
super()
constructor: (@expression) -> super()
compileNode: (o) ->
code = @expression.compile o
@ -1251,29 +1225,20 @@ exports.Parens = class Parens extends Base
children: ['expression']
topSensitive: yes
constructor: (@expression) -> super()
constructor: (@expression) ->
super()
isStatement: (o) ->
@expression.isStatement o
isComplex: ->
@expression.isComplex()
makeReturn: ->
@expression.makeReturn()
unwrap : -> @expression
isComplex : -> @expression.isComplex()
makeReturn: -> @expression.makeReturn()
compileNode: (o) ->
top = del o, 'top'
expr = @expression
if expr instanceof Value and expr.isAtomic()
expr.tags.front = @tags.front
return expr.compile o
bare = o.level < LVL_OP and (expr instanceof Op or expr instanceof Call)
code = expr.compile o, LVL_PAREN
return code if o.level < LVL_OP and (expr instanceof Op or expr instanceof Call)
return (if top then @tab + code + ';' else code) if expr.isStatement o
"(#{code})"
if bare then code else "(#{code})"
#### For
@ -1288,8 +1253,7 @@ exports.For = class For extends Base
children: ['body', 'source', 'guard', 'step', 'from', 'to']
topSensitive: yes
isStatement : YES
isStatement: YES
constructor: (@body, head) ->
if head.index instanceof Value
@ -1316,7 +1280,6 @@ exports.For = class For extends Base
# some cannot.
compileNode: (o) ->
{scope} = o
top = del(o, 'top') and not @returns
name = not @pattern and @name?.compile o
index = @index?.compile o
ivar = if not index then scope.freeVariable 'i' else index
@ -1338,9 +1301,9 @@ exports.For = class For extends Base
if name
[sourcePart, svar] = @source.compileLoopReference o, 'ref'
else
sourcePart = svar = @source.compile o
sourcePart = svar = @source.compile o, LVL_PAREN
namePart = if @pattern
new Assign(@name, new Literal "#{svar}[#{ivar}]").compile merge o, top: on
new Assign(@name, new Literal "#{svar}[#{ivar}]").compile o, LVL_TOP
else if name
"#{name} = #{svar}[#{ivar}]"
unless @object
@ -1351,28 +1314,29 @@ exports.For = class For extends Base
lvar = scope.freeVariable 'len'
vars = "#{ivar} = 0, #{lvar} = #{svar}.length"
cond = "#{ivar} < #{lvar}"
defPart = ''
defPart = ''
if @object
forPart = "#{ivar} in #{sourcePart}"
guardPart = not @raw and
"#{idt}if (!#{ utility 'hasProp' }.call(#{svar}, #{ivar})) continue;\n"
else
vars += ", #{step}" if step isnt pvar
vars += ", #{step}" if step isnt pvar
defPart = "#{@tab}#{sourcePart};\n" if svar isnt sourcePart
forPart = "#{vars}; #{cond}; " + switch +pvar
when 1 then ivar + '++'
when -1 then ivar + '--'
else ivar + if pvar < 0 then ' -= ' + pvar.slice 1 else ' += ' + pvar
unless top
forPart = "#{vars}; #{cond}; " + ivar + switch +pvar
when 1 then '++'
when -1 then '--'
else (if pvar < 0 then ' -= ' + pvar.slice 1 else ' += ' + pvar)
if o.level > LVL_TOP or @returns
rvar = scope.freeVariable 'result'
defPart += "#{@tab}#{rvar} = [];\n"
resultRet = @compileReturnValue rvar, o
body = Push.wrap rvar, body
body = Expressions.wrap [new If @guard, body] if @guard
varPart = "#{idt}#{namePart};\n" if namePart
body = Expressions.wrap [new If @guard, body] if @guard
varPart = "#{idt}#{namePart};\n" if namePart
o.indent = idt
"""
#{ defPart or '' }#{@tab}for (#{forPart}) {
#{ guardPart or '' }#{varPart}#{ body.compile merge o, indent: idt, top: on }
#{ defPart or '' }#{@tab}for (#{forPart}) {
#{ guardPart or '' }#{varPart}#{ body.compile o, LVL_TOP }
#{@tab}}#{ resultRet or '' }
"""
@ -1385,29 +1349,28 @@ exports.Switch = class Switch extends Base
isStatement: YES
constructor: (@subject, @cases, @otherwise) ->
super()
constructor: (@subject, @cases, @otherwise) -> super()
makeReturn: ->
pair[1].makeReturn() for pair in @cases
@otherwise.makeReturn() if @otherwise
@otherwise?.makeReturn()
this
compileNode: (o) ->
idt1 = @idt 1
idt2 = o.indent = @idt 2
o.top = yes
code = "#{ @tab }switch (#{ @subject?.compile(o) or true }) {"
for [conditions, block] in @cases
for condition in flatten [conditions]
condition = condition.invert().invert() unless @subject
code += "\n#{ idt1 }case #{ condition.compile o }:"
code += "\n#{ block.compile o }"
code += "\n#{ idt2 }break;" unless last(block.expressions) instanceof Return
if @otherwise
code += "\n#{ idt1 }default:\n#{ @otherwise.compile o }"
code += "\n#{ @tab }}"
code
idt1 = @idt 1
idt2 = o.indent = @idt 2
code = @tab + "switch (#{ @subject?.compile(o, LVL_PAREN) or true }) {\n"
for [conditions, block], i in @cases
for cond in flatten [conditions]
cond = cond.invert().invert() unless @subject
code += idt1 + "case #{ cond.compile o, LVL_PAREN }:\n"
code += block.compile(o, LVL_TOP) + '\n'
break if i is @cases.length - 1 and not @otherwise
for expr in block.expressions by -1 when expr not instanceof Comment
code += idt2 + 'break;\n' unless expr instanceof Return
break
code += idt1 + "default:\n#{ @otherwise.compile o, LVL_TOP }\n" if @otherwise
code + @tab + '}'
#### If
@ -1420,8 +1383,6 @@ exports.If = class If extends Base
children: ['condition', 'body', 'elseBody']
topSensitive: yes
constructor: (condition, @body, tags) ->
@tags = tags or= {}
@condition = if tags.invert then condition.invert() else condition
@ -1444,7 +1405,7 @@ exports.If = class If extends Base
# The **If** only compiles into a statement if either of its bodies needs
# to be a statement. Otherwise a conditional operator is safe.
isStatement: (o) ->
@statement or= o?.top or @bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)
o?.level is LVL_TOP or @bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)
compileNode: (o) ->
if @isStatement o then @compileStatement o else @compileExpression o
@ -1463,24 +1424,23 @@ exports.If = class If extends Base
# Compile the **If** as a regular *if-else* statement. Flattened chains
# force inner *else* bodies into statement form.
compileStatement: (o) ->
top = del o, 'top'
child = del o, 'chainChild'
condO = merge o
cond = @condition.compile o, LVL_PAREN
o.indent = @idt 1
o.top = true
ifPart = "if (#{ @condition.compile condO, LVL_PAREN }) {\n#{ @body.compile o }\n#{@tab}}"
body = @ensureExpressions(@body).compile o
ifPart = "if (#{cond}) {\n#{body}\n#{@tab}}"
ifPart = @tab + ifPart unless child
return ifPart unless @elseBody
ifPart + ' else ' + if @isChain
@elseBodyNode().compile merge o, indent: @tab, chainChild: true
else
"{\n#{ @elseBody.compile o }\n#{@tab}}"
"{\n#{ @elseBody.compile o, LVL_TOP }\n#{@tab}}"
# Compile the If as a conditional operator.
compileExpression: (o) ->
code = @condition .compile(o, LVL_COND) + ' ? ' +
@bodyNode().compile(o, LVL_ASSIGN) + ' : ' +
@elseBodyNode()?.compile o, LVL_ASSIGN
code = @condition .compile(o, LVL_COND) + ' ? ' +
@bodyNode().compile(o, LVL_LIST) + ' : ' +
@elseBodyNode()?.compile o, LVL_LIST
if o.level >= LVL_COND then "(#{code})" else code
unfoldSoak: -> @soakNode and this
@ -1543,7 +1503,7 @@ UTILITIES =
# Correctly set up a prototype chain for inheritance, including a reference
# to the superclass for `super()` calls. See:
# [goog.inherits](http://closure-library.googlecode.com/svn/docs/closureGoogBase.js.source.html#line1206).
extends: '''
extends: '''
function(child, parent) {
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
@ -1570,16 +1530,15 @@ UTILITIES =
# Shortcuts to speed up the lookup time for native functions.
hasProp: 'Object.prototype.hasOwnProperty'
slice: 'Array.prototype.slice'
slice : 'Array.prototype.slice'
# Levels indicates a node's position in the AST.
LVL_TOP = 0 # ...;
LVL_PAREN = 1 # (...)
LVL_LIST = 2 # [...]
LVL_ASSIGN = 3 # x = ...
LVL_COND = 4 # ... ? a : b
LVL_OP = 5 # +...
LVL_ACCESS = 6 # ...[0]
LVL_COND = 3 # ... ? x : y
LVL_OP = 4 # !...
LVL_ACCESS = 5 # ...[0]
# Tabs are two spaces for pretty printing.
TAB = ' '
@ -1603,3 +1562,5 @@ utility = (name) ->
ref = "__#{name}"
Scope.root.assign ref, UTILITIES[name]
ref
multident = (code, tab) -> code.replace /\n/g, '$&' + tab