destructuring assignment no longer uses a temporary variable for simple LHS

This commit is contained in:
satyr
2010-10-11 09:17:31 +09:00
parent 2642fde0f8
commit e5fe145f80
3 changed files with 102 additions and 48 deletions

View File

@@ -30,11 +30,11 @@
return (new Rewriter).rewrite(this.tokens); return (new Rewriter).rewrite(this.tokens);
}; };
Lexer.prototype.identifierToken = function() { Lexer.prototype.identifierToken = function() {
var _ref2, colon, forcedIdentifier, id, input, match, tag; var colon, forcedIdentifier, id, input, match, tag;
if (!(match = IDENTIFIER.exec(this.chunk))) { if (!(match = IDENTIFIER.exec(this.chunk))) {
return false; return false;
} }
_ref2 = match, input = _ref2[0], id = _ref2[1], colon = _ref2[2]; input = match[0], id = match[1], colon = match[2];
this.i += input.length; this.i += input.length;
if (id === 'all' && this.tag() === 'FOR') { if (id === 'all' && this.tag() === 'FOR') {
this.token('ALL', id); this.token('ALL', id);
@@ -153,11 +153,11 @@
return true; return true;
}; };
Lexer.prototype.commentToken = function() { Lexer.prototype.commentToken = function() {
var _ref2, comment, here, match; var comment, here, match;
if (!(match = this.chunk.match(COMMENT))) { if (!(match = this.chunk.match(COMMENT))) {
return false; return false;
} }
_ref2 = match, comment = _ref2[0], here = _ref2[1]; comment = match[0], here = match[1];
this.line += count(comment, '\n'); this.line += count(comment, '\n');
this.i += comment.length; this.i += comment.length;
if (here) { if (here) {
@@ -197,8 +197,8 @@
return true; return true;
}; };
Lexer.prototype.heregexToken = function(match) { Lexer.prototype.heregexToken = function(match) {
var _i, _len, _ref2, _ref3, _ref4, _ref5, _this, body, flags, heregex, re, tag, tokens, value; var _i, _len, _ref2, _ref3, _ref4, _this, body, flags, heregex, re, tag, tokens, value;
_ref2 = match, heregex = _ref2[0], body = _ref2[1], flags = _ref2[2]; heregex = match[0], body = match[1], flags = match[2];
this.i += heregex.length; this.i += heregex.length;
if (0 > body.indexOf('#{')) { if (0 > body.indexOf('#{')) {
re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/'); re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/');
@@ -208,10 +208,10 @@
this.token('IDENTIFIER', 'RegExp'); this.token('IDENTIFIER', 'RegExp');
this.tokens.push(['CALL_START', '(']); this.tokens.push(['CALL_START', '(']);
tokens = []; tokens = [];
for (_i = 0, _len = (_ref3 = this.interpolateString(body, { for (_i = 0, _len = (_ref2 = this.interpolateString(body, {
regex: true regex: true
})).length; _i < _len; _i++) { })).length; _i < _len; _i++) {
_ref4 = _ref3[_i], tag = _ref4[0], value = _ref4[1]; _ref3 = _ref2[_i], tag = _ref3[0], value = _ref3[1];
if (tag === 'TOKENS') { if (tag === 'TOKENS') {
tokens.push.apply(tokens, value); tokens.push.apply(tokens, value);
} else { } else {
@@ -224,7 +224,7 @@
tokens.push(['+', '+']); tokens.push(['+', '+']);
} }
tokens.pop(); tokens.pop();
if ((((_ref5 = tokens[0]) != null) ? _ref5[0] : undefined) !== 'STRING') { if ((((_ref4 = tokens[0]) != null) ? _ref4[0] : undefined) !== 'STRING') {
this.tokens.push(['STRING', '""'], ['+', '+']); this.tokens.push(['STRING', '""'], ['+', '+']);
} }
(_this = this.tokens).push.apply(_this, tokens); (_this = this.tokens).push.apply(_this, tokens);
@@ -399,15 +399,15 @@
return true; return true;
}; };
Lexer.prototype.sanitizeHeredoc = function(doc, options) { Lexer.prototype.sanitizeHeredoc = function(doc, options) {
var _ref2, _ref3, attempt, herecomment, indent, match; var _ref2, attempt, herecomment, indent, match;
_ref2 = options, indent = _ref2.indent, herecomment = _ref2.herecomment; indent = options.indent, herecomment = options.herecomment;
if (herecomment && 0 > doc.indexOf('\n')) { if (herecomment && 0 > doc.indexOf('\n')) {
return doc; return doc;
} }
if (!herecomment) { if (!herecomment) {
while (match = HEREDOC_INDENT.exec(doc)) { while (match = HEREDOC_INDENT.exec(doc)) {
attempt = match[1]; attempt = match[1];
if (indent === null || (0 < (_ref3 = attempt.length)) && (_ref3 < indent.length)) { if (indent === null || (0 < (_ref2 = attempt.length)) && (_ref2 < indent.length)) {
indent = attempt; indent = attempt;
} }
} }
@@ -452,7 +452,7 @@
throw SyntaxError("Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned"); throw SyntaxError("Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned");
}; };
Lexer.prototype.balancedString = function(str, delimited, options) { Lexer.prototype.balancedString = function(str, delimited, options) {
var _i, _len, _ref2, close, i, levels, open, pair, slen; var _i, _len, close, i, levels, open, pair, slen;
options || (options = {}); options || (options = {});
levels = []; levels = [];
i = 0; i = 0;
@@ -463,7 +463,7 @@
} else { } else {
for (_i = 0, _len = delimited.length; _i < _len; _i++) { for (_i = 0, _len = delimited.length; _i < _len; _i++) {
pair = delimited[_i]; pair = delimited[_i];
_ref2 = pair, open = _ref2[0], close = _ref2[1]; open = pair[0], close = pair[1];
if (levels.length && starts(str, close, i) && last(levels) === pair) { if (levels.length && starts(str, close, i) && last(levels) === pair) {
levels.pop(); levels.pop();
i += close.length - 1; i += close.length - 1;

View File

@@ -154,6 +154,7 @@
Base.prototype.isPureStatement = NO; Base.prototype.isPureStatement = NO;
Base.prototype.isComplex = YES; Base.prototype.isComplex = YES;
Base.prototype.topSensitive = NO; Base.prototype.topSensitive = NO;
Base.prototype.assigns = NO;
return Base; return Base;
})(); })();
exports.Expressions = (function() { exports.Expressions = (function() {
@@ -266,6 +267,9 @@
Literal.prototype.isReserved = function() { Literal.prototype.isReserved = function() {
return !!this.value.reserved; return !!this.value.reserved;
}; };
Literal.prototype.assigns = function(name) {
return name === this.value;
};
Literal.prototype.compileNode = function(o) { Literal.prototype.compileNode = function(o) {
var end, idt, val; var end, idt, val;
idt = this.isStatement(o) ? this.idt() : ''; idt = this.isStatement(o) ? this.idt() : '';
@@ -348,6 +352,9 @@
Value.prototype.isComplex = function() { Value.prototype.isComplex = function() {
return this.base.isComplex() || this.hasProperties(); return this.base.isComplex() || this.hasProperties();
}; };
Value.prototype.assigns = function(name) {
return !this.properties.length && this.base.assigns(name);
};
Value.prototype.makeReturn = function() { Value.prototype.makeReturn = function() {
return this.properties.length ? Value.__super__.makeReturn.call(this) : this.base.makeReturn(); return this.properties.length ? Value.__super__.makeReturn.call(this) : this.base.makeReturn();
}; };
@@ -817,13 +824,7 @@
for (i = 0, _len = (_ref2 = this.properties).length; i < _len; i++) { for (i = 0, _len = (_ref2 = this.properties).length; i < _len; i++) {
prop = _ref2[i]; prop = _ref2[i];
_result.push((function() { _result.push((function() {
join = ",\n"; join = i === this.properties.length - 1 ? '' : (prop === lastNoncom || prop instanceof Comment ? '\n' : ',\n');
if ((prop === lastNoncom) || (prop instanceof Comment)) {
join = "\n";
}
if (i === this.properties.length - 1) {
join = '';
}
indent = prop instanceof Comment ? '' : this.idt(1); indent = prop instanceof Comment ? '' : this.idt(1);
if (prop instanceof Value && prop.tags["this"]) { if (prop instanceof Value && prop.tags["this"]) {
prop = new Assign(prop.properties[0].name, prop, 'object'); prop = new Assign(prop.properties[0].name, prop, 'object');
@@ -836,9 +837,19 @@
return _result; return _result;
}).call(this); }).call(this);
props = props.join(''); props = props.join('');
obj = '{' + (props ? '\n' + props + '\n' + this.idt() : '') + '}'; obj = ("{" + (props ? '\n' + props + '\n' + this.idt() : '') + "}");
return top ? ("(" + obj + ")") : obj; return top ? ("(" + obj + ")") : obj;
}; };
ObjectLiteral.prototype.assigns = function(name) {
var _i, _len, _ref2, prop;
for (_i = 0, _len = (_ref2 = this.properties).length; _i < _len; _i++) {
prop = _ref2[_i];
if (prop.assigns(name)) {
return true;
}
}
return false;
};
return ObjectLiteral; return ObjectLiteral;
})(); })();
exports.ArrayLiteral = (function() { exports.ArrayLiteral = (function() {
@@ -874,6 +885,16 @@
objects = objects.join(''); objects = objects.join('');
return 0 < objects.indexOf('\n') ? ("[\n" + (o.indent) + objects + "\n" + (this.tab) + "]") : ("[" + objects + "]"); return 0 < objects.indexOf('\n') ? ("[\n" + (o.indent) + objects + "\n" + (this.tab) + "]") : ("[" + objects + "]");
}; };
ArrayLiteral.prototype.assigns = function(name) {
var _i, _len, _ref2, obj;
for (_i = 0, _len = (_ref2 = this.objects).length; _i < _len; _i++) {
obj = _ref2[_i];
if (obj.assigns(name)) {
return true;
}
}
return false;
};
return ArrayLiteral; return ArrayLiteral;
})(); })();
exports.Class = (function() { exports.Class = (function() {
@@ -1024,7 +1045,7 @@
return top || this.parenthetical ? val : ("(" + val + ")"); return top || this.parenthetical ? val : ("(" + val + ")");
}; };
Assign.prototype.compilePatternMatch = function(o) { Assign.prototype.compilePatternMatch = function(o) {
var _len, _ref2, _ref3, accessClass, assigns, code, i, idx, isObject, obj, objects, olength, otop, splat, top, val, valVar, value; var _len, _ref2, _ref3, accessClass, assigns, code, i, idx, isObject, obj, objects, olength, otop, ref, splat, top, val, valVar, value;
if ((value = this.value).isStatement(o)) { if ((value = this.value).isStatement(o)) {
value = Closure.wrap(value); value = Closure.wrap(value);
} }
@@ -1050,15 +1071,19 @@
otop = merge(o, { otop = merge(o, {
top: true top: true
}); });
valVar = o.scope.freeVariable('ref'); valVar = value.compile(o);
assigns = [("" + valVar + " = " + (value.compile(o)))]; assigns = [];
splat = false; splat = false;
if (!IDENTIFIER.test(valVar) || this.variable.assigns(valVar)) {
assigns.push("" + (ref = o.scope.freeVariable('ref')) + " = " + valVar);
valVar = ref;
}
for (i = 0, _len = objects.length; i < _len; i++) { for (i = 0, _len = objects.length; i < _len; i++) {
obj = objects[i]; obj = objects[i];
idx = i; idx = i;
if (isObject) { if (isObject) {
if (obj instanceof Assign) { if (obj instanceof Assign) {
_ref3 = [obj.value, obj.variable.base], obj = _ref3[0], idx = _ref3[1]; _ref3 = obj, idx = _ref3.variable.base, obj = _ref3.value;
} else { } else {
idx = obj.tags["this"] ? obj.properties[0].name : obj; idx = obj.tags["this"] ? obj.properties[0].name : obj;
} }
@@ -1095,6 +1120,9 @@
val = this.value.compile(o); val = this.value.compile(o);
return "([].splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + ref + " = " + val + ")), " + ref + ")"; return "([].splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + ref + " = " + val + ")), " + ref + ")";
}; };
Assign.prototype.assigns = function(name) {
return this[this.context === 'object' ? 'value' : 'variable'].assigns(name);
};
return Assign; return Assign;
})(); })();
exports.Code = (function() { exports.Code = (function() {
@@ -1221,16 +1249,16 @@
Splat = (function() { Splat = (function() {
function Splat(name) { function Splat(name) {
Splat.__super__.constructor.call(this); Splat.__super__.constructor.call(this);
if (!name.compile) { this.name = name.compile ? name : new Literal(name);
name = new Literal(name);
}
this.name = name;
return this; return this;
}; };
return Splat; return Splat;
})(); })();
__extends(Splat, Base); __extends(Splat, Base);
Splat.prototype.children = ['name']; Splat.prototype.children = ['name'];
Splat.prototype.assigns = function(name) {
return this.name.assigns(name);
};
Splat.prototype.compileNode = function(o) { Splat.prototype.compileNode = function(o) {
return (this.index != null) ? this.compileParam(o) : this.name.compile(o); return (this.index != null) ? this.compileParam(o) : this.name.compile(o);
}; };
@@ -1611,16 +1639,16 @@
exports.For = (function() { exports.For = (function() {
For = (function() { For = (function() {
function For(_arg, source, _arg2, _arg3) { function For(_arg, source, _arg2, _arg3) {
var _ref2, _ref3; var _ref2;
this.index = _arg3; this.index = _arg3;
this.name = _arg2; this.name = _arg2;
this.body = _arg; this.body = _arg;
For.__super__.constructor.call(this); For.__super__.constructor.call(this);
_ref2 = source, this.source = _ref2.source, this.guard = _ref2.guard, this.step = _ref2.step; this.source = source.source, this.guard = source.guard, this.step = source.step;
this.raw = !!source.raw; this.raw = !!source.raw;
this.object = !!source.object; this.object = !!source.object;
if (this.object) { if (this.object) {
_ref3 = [this.index, this.name], this.name = _ref3[0], this.index = _ref3[1]; _ref2 = [this.index, this.name], this.name = _ref2[0], this.index = _ref2[1];
} }
this.pattern = this.name instanceof Value; this.pattern = this.name instanceof Value;
if (this.index instanceof Value) { if (this.index instanceof Value) {
@@ -1772,16 +1800,16 @@
return this; return this;
}; };
Switch.prototype.compileNode = function(o) { Switch.prototype.compileNode = function(o) {
var _i, _j, _len, _len2, _ref2, _ref3, _ref4, block, code, condition, conditions, exprs, idt, pair; var _i, _j, _len, _len2, _ref2, _ref3, block, code, condition, conditions, exprs, idt, pair;
idt = (o.indent = this.idt(2)); idt = (o.indent = this.idt(2));
o.top = true; o.top = true;
code = ("" + (this.tab) + "switch (" + (this.subject.compile(o)) + ") {"); code = ("" + (this.tab) + "switch (" + (this.subject.compile(o)) + ") {");
for (_i = 0, _len = (_ref2 = this.cases).length; _i < _len; _i++) { for (_i = 0, _len = (_ref2 = this.cases).length; _i < _len; _i++) {
pair = _ref2[_i]; pair = _ref2[_i];
_ref3 = pair, conditions = _ref3[0], block = _ref3[1]; conditions = pair[0], block = pair[1];
exprs = block.expressions; exprs = block.expressions;
for (_j = 0, _len2 = (_ref4 = flatten([conditions])).length; _j < _len2; _j++) { for (_j = 0, _len2 = (_ref3 = flatten([conditions])).length; _j < _len2; _j++) {
condition = _ref4[_j]; condition = _ref3[_j];
if (this.tags.subjectless) { if (this.tags.subjectless) {
condition = new Op('!!', new Parens(condition)); condition = new Op('!!', new Parens(condition));
} }

View File

@@ -143,6 +143,9 @@ exports.Base = class Base
isComplex : YES isComplex : YES
topSensitive : NO topSensitive : NO
# Is this node used to assign a certain variable?
assigns: NO
#### Expressions #### Expressions
# The expressions body is the list of expressions that forms the body of an # The expressions body is the list of expressions that forms the body of an
@@ -253,6 +256,8 @@ exports.Literal = class Literal extends Base
isReserved: -> isReserved: ->
!!@value.reserved !!@value.reserved
assigns: (name) -> name is @value
compileNode: (o) -> compileNode: (o) ->
idt = if @isStatement(o) then @idt() else '' idt = if @isStatement(o) then @idt() else ''
end = if @isStatement(o) then ';' else '' end = if @isStatement(o) then ';' else ''
@@ -324,6 +329,9 @@ exports.Value = class Value extends Base
isComplex: -> isComplex: ->
@base.isComplex() or @hasProperties() @base.isComplex() or @hasProperties()
assigns: (name) ->
not @properties.length and @base.assigns name
makeReturn: -> makeReturn: ->
if @properties.length then super() else @base.makeReturn() if @properties.length then super() else @base.makeReturn()
@@ -702,12 +710,15 @@ exports.ObjectLiteral = class ObjectLiteral extends Base
compileNode: (o) -> compileNode: (o) ->
top = del o, 'top' top = del o, 'top'
o.indent = @idt 1 o.indent = @idt 1
nonComments = prop for prop in @properties when (prop not instanceof Comment) nonComments = prop for prop in @properties when prop not instanceof Comment
lastNoncom = last nonComments lastNoncom = last nonComments
props = for prop, i in @properties props = for prop, i in @properties
join = ",\n" join = if i is @properties.length - 1
join = "\n" if (prop is lastNoncom) or (prop instanceof Comment) ''
join = '' if i is @properties.length - 1 else if prop is lastNoncom or prop instanceof Comment
'\n'
else
',\n'
indent = if prop instanceof Comment then '' else @idt 1 indent = if prop instanceof Comment then '' else @idt 1
if prop instanceof Value and prop.tags.this if prop instanceof Value and prop.tags.this
prop = new Assign prop.properties[0].name, prop, 'object' prop = new Assign prop.properties[0].name, prop, 'object'
@@ -715,9 +726,13 @@ exports.ObjectLiteral = class ObjectLiteral extends Base
prop = new Assign prop, prop, 'object' prop = new Assign prop, prop, 'object'
indent + prop.compile(o) + join indent + prop.compile(o) + join
props = props.join('') props = props.join('')
obj = '{' + (if props then '\n' + props + '\n' + @idt() else '') + '}' obj = "{#{ if props then '\n' + props + '\n' + @idt() else '' }}"
if top then "(#{obj})" else obj if top then "(#{obj})" else obj
assigns: (name) ->
for prop in @properties when prop.assigns name then return yes
no
#### ArrayLiteral #### ArrayLiteral
# An array literal. # An array literal.
@@ -752,6 +767,10 @@ exports.ArrayLiteral = class ArrayLiteral extends Base
else else
"[#{objects}]" "[#{objects}]"
assigns: (name) ->
for obj in @objects when obj.assigns name then return yes
no
#### Class #### Class
# The CoffeeScript class definition. # The CoffeeScript class definition.
@@ -898,16 +917,19 @@ exports.Assign = class Assign extends Base
return new Assign(obj, value).compile o return new Assign(obj, value).compile o
top = del o, 'top' top = del o, 'top'
otop = merge o, top: yes otop = merge o, top: yes
valVar = o.scope.freeVariable 'ref' valVar = value.compile o
assigns = ["#{valVar} = #{ value.compile o }"] assigns = []
splat = false splat = false
if not IDENTIFIER.test(valVar) or @variable.assigns(valVar)
assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{valVar}"
valVar = ref
for obj, i in objects for obj, i in objects
# A regular array pattern-match. # A regular array pattern-match.
idx = i idx = i
if isObject if isObject
if obj instanceof Assign if obj instanceof Assign
# A regular object pattern-match. # A regular object pattern-match.
[obj, idx] = [obj.value, obj.variable.base] {variable: {base: idx}, value: obj} = obj
else else
# A shorthand `{a, b, @c} = val` pattern-match. # A shorthand `{a, b, @c} = val` pattern-match.
idx = if obj.tags.this then obj.properties[0].name else obj idx = if obj.tags.this then obj.properties[0].name else obj
@@ -937,6 +959,9 @@ exports.Assign = class Assign extends Base
val = @value.compile(o) val = @value.compile(o)
"([].splice.apply(#{name}, [#{from}, #{to}].concat(#{ref} = #{val})), #{ref})" "([].splice.apply(#{name}, [#{from}, #{to}].concat(#{ref} = #{val})), #{ref})"
assigns: (name) ->
@[if @context is 'object' then 'value' else 'variable'].assigns name
#### Code #### Code
# A function definition. This is the only node that creates a new Scope. # A function definition. This is the only node that creates a new Scope.
@@ -1039,8 +1064,9 @@ exports.Splat = class Splat extends Base
constructor: (name) -> constructor: (name) ->
super() super()
name = new Literal name unless name.compile @name = if name.compile then name else new Literal name
@name = name
assigns: (name) -> @name.assigns name
compileNode: (o) -> compileNode: (o) ->
if @index? then @compileParam(o) else @name.compile(o) if @index? then @compileParam(o) else @name.compile(o)