nodes: refactored and removed the notion of `o.top` in favor of LVL_TOP
This commit is contained in:
parent
b82f495ec7
commit
2bc2c4717e
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -382,7 +382,6 @@
|
|||
break;
|
||||
case '::':
|
||||
prev[0] = 'INDEX_PROTO';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
376
lib/nodes.js
376
lib/nodes.js
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
401
src/nodes.coffee
401
src/nodes.coffee
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue